应用程序在iPhone 5.1模拟器和iOS 5.1的真实iPhone 4上performance不同

简而言之:

当导航控制器的后退button的标题被​​改变时,在某些情况下旧的标题被卡住,新的标题将不被显示。 这只会发生在一些可再生的情况下,而其他情况下的devise也是如此。

这取决于硬件

  • 错误发生在iPhone 3G(iOS 4.2.1)和模拟器(iOS 5.1)
  • 使用相同的源代码,iPhone 4(iOS 5.1)没有错误

这取决于写在标题上的字

  • 当button被创build时,它会从我自己写的创build方法中得到与它自动获得的标题相同的单词(即导航控制器堆栈中前一页的标题),并且当其他情况匹配时,当稍后尝试更改button的标题时,旧文本将卡住,新标题将不显示。
  • 在创build时,button会获得一个与默认标题不同的标题,然后每一个标题的更改都可以正常工作,只要不指定默认标题即可。
  • 如果经过很多不同标题的成功改变,你把这个单词放在button标题上,那是它的默认标题,那么这个单词就卡住了。 以后的更改将不被接受(没有任何消息,只有在其他情况相匹配的情况下)

这取决于button是否在此期间不可见。

  • 如果在导航控制器堆栈上推送了另一个视图,以便具有有缺陷button的旧页面被新页面隐藏起来,并且当新页面从堆栈中popup时再次使得该button再次可见时(以及其他情况相匹配),然后旧的文本被卡住,试验改变它被忽略(没有任何消息)。
  • 如果button是更新隐藏的,改变它的标题永远不是问题。 我总是工作。

在animation过程中可以看到正确的标题

  • 当由于上述情况的组合而忽略了改变后退button标题的尝试时,当该后退button被击中并且页面的幻灯片到右侧animation是正确的标题总是变为可见约0.3秒处理。 在animation开始时,旧的卡住标题被replace为正确的标题,并且在animation期间正确的标题是可见的。

详细说明

这是关于UINavigationController的后退button上的文字。 我根据新的语言设置更改此button标题。 目前我的应用程序在导航控制器堆栈中最多有3个视图控制器。 它们中的每一个都是UITableViewController的一个不同的子类。

表1,名为GeneralTableVC是堆栈上的根视图。 它没有后退button。 它向用户提供了他在应用程序内部存储的摘要,并显示带有设置button的工具栏。

表1中提供了该工具栏的导航控制器。它在表2和3中设置为不可见。此时在该工具栏中只有一个名为“设置”的button。 触摸这个设置button将把表2推入堆栈。

表2,名为SettingsTabVC有一个后退button,这是模拟器中的问题,但在我运行iOS 5.1的真正的iPhone 4上工作正常。

通过触摸表2的第一行,将创build新的表(表3)并将其推入堆栈。

表3,名为LangSelectTableVC也有一个后退button,但是这个在两个设备,iPhone模拟器和真正的iPhone 4中工作得很好。

表3是一个语言select表,它显示了所有可用语言的列表(目前只有英语和德语)。 触摸一行可立即更改设置。 活动视图(表3)将被重新绘制,并且在几毫秒内,屏幕上的所有文本都将以新语言显示。

重绘表格本身并没有问题,还有导航栏中的标题。 但后退button上的文字也必须翻译,这有点棘手。 我在两个后退button上都做了同样的诀窍,它可以很好地工作在表3中指向表2的可见button。但是,使用非常相同的代码,模拟器中存在问题(但不是真正的iPhone),并使用表2中的button指向表1。

我给你一些代码片段和一些截图,告诉你我做了什么,发生了什么:


源代码

ARC(自动引用计数)正在使用中。

我确实定义了一个重画协议:

Protocols.h

 #ifndef ToDo_Project_Protocols_h #define ToDo_Project_Protocols_h @protocol redrawProt - (void) mustRedraw; @end #endif 

这是表1的标题:

GeneralTableVC.h

 #import <UIKit/UIKit.h> #import "Protocols.h" // some other imports @interface GeneralTabVC : UITableViewController <redrawProt> @property id<redrawProt> parent; @property Boolean mustRedrawMyself; @property NSString* backTitle; @property UIBarButtonItem* myBackButton; @property UIBarButtonItem* parBackButton; - (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem*)bB; @end 

其他表头文件SettingsTabVC.hLangSelectTabVC.h定义了相同的属性和相同的init函数

程序从这里开始:

是AppDelegate.m的一部分

 - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // some code GeneralTabVC* genTabCon = [[GeneralTabVC alloc] initWithParent:nil andBackTitle:nil andBackButton:nil]; UINavigationController* navCon = [[UINavigationController alloc] initWithRootViewController:genTabCon]; // some other code } 

接下来是表1( GeneralTableVC.m )的实现。 表2( SettingsTabVC.m )和表3( LangSelectTabVC.m )中的代码类似地是相同的。 我没有显示那些实现了协议UITableViewDataSource的代码部分。 我认为这些部分对于解释问题并不重要。

在这段代码中,你会发现macrosLocalizedString(keyword) ,它和NSLocalizedString(keyword,comment)完全一样,它将关键字翻译成所需的语言。 我的这个macros的版本使用不同的bundel进行翻译(不是主包)

GeneralTableVC.m

 #import "GeneralTabVC.h" #import "SettingsTabVC.h" #define MYTITLE @"summary" id<redrawProt> parent; Boolean mustRedrawMyself; NSString* backTitle; UIBarButtonItem* myBackButton; UIBarButtonItem* parBackButton; @interface GeneralTabVC () @end @implementation GeneralTabVC @synthesize parent, mustRedrawMyself, backTitle, myBackButton, parBackButton; - (void) mustRedraw { self.mustRedrawMyself = YES; } - (void) redraw { if ((self.parBackButton) && (self.backTitle)) { // Important! // here I change the back buttons title! self.parBackButton.title = LocalizedString(self.backTitle); } if (self.parent) { [self.parent mustRedraw]; } self.title = LocalizedString(MYTITLE); [self.tableView reloadData]; self.mustRedrawMyself = NO; } - (id) initWithParent:(id<redrawProt>)par andBackTitle:(NSString*)bT andBackButton:(UIBarButtonItem *)bB { self = [super initWithStyle:UITableViewStyleGrouped]; if (self) { self.parent = par; self.mustRedrawMyself = NO; self.backTitle = bT; self.parBackButton = bB; } return self; } - (void) toolbarInit { // this method exists only in Table 1, not in other tables // it creates a UIBarButtonItem, adds it to self.toolbarItems // and makes it visible } - (void)SettingsAction:(id)sender { // this method exists only in Table 1, not in other tables // it will be executed after the user tabs on the settings- // button in the toolbar SettingsTabVC* setTabCon = [[SettingsTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton]; [self.navigationController pushViewController:setTabCon animated:YES]; } - (void) viewDidLoad { [super viewDidLoad]; self.title = LocalizedString(MYTITLE); // I want an Edit-Button. Localization of this button is // not yet done. At the moment is uses the systems language, // not the apps language. self.navigationItem.rightBarButtonItem = self.editButtonItem; [self toolbarInit]; } - (void) viewWillAppear:(BOOL)animated { // this is an important method! Maybe here is the reason for // my problem! [super viewWillAppear:animated]; // When ever this controllers view is going to appear, and // when ever it is necessary to redraw it in a new language, // it will redraw itself: if (self.mustRedrawMyself) { [self redraw]; } // And here comes the buggy back button: // When ever this controllers view is going to appear, // a new back button will be created with a title in the // new language: UIBarButtonItem* BB = [[UIBarButtonItem alloc] init]; BB.title = LocalizedString(MYTITLE); self.myBackButton = BB; self.navigationItem.backBarButtonItem = self.myBackButton; } - (void) viewDidAppear:(BOOL)animated { [super viewDidAppear:animated]; // show toolbar: [self.navigationController setToolbarHidden:NO animated:YES]; } // next methods are about InterfaceOrientation and the // UITableViewDataSource protocoll. They are not important // for the problem. // but maybe the very last method is important. It comes in // different versions in the three implementation files: - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // This is the version of GeneralTableVC.m (Table 1) // It does nothing (at the actual stage of expansion, in later // versions it will start the main business logic of this app) } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // This is the version of SettingsTableVC.m (Table 2) // Tabbing onto row 0 of section 0 will push the // language-selection-table (Table 3) on screen: if (indexPath.section == 0) { if (indexPath.row == 0) { // create Table 3: LangSelectTabVC* langTabCon = [[LangSelectTabVC alloc] initWithParent:self andBackTitle:MYTITLE andBackButton:self.myBackButton]; [self.navigationController pushViewController:langTabCon animated:YES]; } else { // do something else (nothing at this stage of expansion) } } else { // do something else (nothing at this stage of expansion) } } - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { // This is the version of LangSelectTableVC.m (Table 3) // here I do some magic to select and store the new language. // Part of this magic is transforming indexPath.row // into a valid language-code, putting it into the // settings-object, and registering this object to // NSUserDefaults } @end 

截图

在iPhone 5.1模拟器上启动应用程序将在屏幕上显示表1( GeneralTableVC ):

启动应用程序后

在屏幕button的工具栏中,在右侧find设置button。 按下这个button会在屏幕上显示下一个表格:

设置屏幕

观看标题栏中的后退button。 它显示文本“摘要”,这是正确的,因为上一个表格标题是“摘要”。

现在我们select第一行(“ Language English > ”):

在改变语言之前

一切安好。 现在让我们改变语言。 在“ German ”上标签:

改变语言后

哇! 现在一切都在德国。 即使后退button已从“设置”更改为“Einstellungen”。

让我们看看这个“Einstellungen”的后退button:

返回按钮在模拟器中有错误的语言

现在差不多了, 一切都变成了德语。 一切,但后退button,仍然说“摘要”,而不是“Überblick”。 我不明白为什么,因为当我用真正的iPhone 4完全相同的源代码完成相同的步骤时,最后一个屏幕看起来像这样:

正确的语言在真正的iPhone上

注意后退button上的文字。 在真正的iPhone 4上是德语单词“Überblick”(这是我想要的),但在模拟器中是英文单词“Summary”。 这对我来说,在一些手机(比如我的iPhone 4)上,用户可以获得预期的效果,但是也可能在其他手机上(也许是iPhone 4S),用户可以获得越野车显示。

有没有人知道我的代码有什么问题?


编辑

编辑: 2012-04-06 09:04 +02:00(中欧夏令时)

我确实设法在另一块硬件上testing我的应用程序,一个旧的iPhone 3G(iOS 4.2.1)在旧的iPhone上,我的应用程序的行为与模拟器中的行为完全相同。 在iPhone 4上运行相同的应用程序会产生不同的行为。

更确切地说:

  • 在iPhone 4(iOS 5.1)上:应用程序正在做我想要的,没有错误的行为。
  • 在模拟器(iOS 5.1)上:应用程序在导航控制器后退button上显示错误的标题。
  • 在iPhone 3G(iOS 4.2.1)上:应用程序显示与模拟器相同的故障行为。

编辑: 2012-04-07 10:14 +02:00(中欧夏令时)

通过观察iPhone 3G上的转换,我发现了一些有趣的东西,也许有帮助:当我用错误的文本标签button时,会发生以下情况:

  1. 错误的文本被replace为正确的文本
  2. 在这个replace之后,视图消失animation(向右滑动),并且底层视图变为可见。 这个转换的持续时间大约为0.3秒,在这个短的时间间隔内,正确的文本在所有的硬件iPhone和模拟器中都是可见的。

但问题依然是:为什么在iPhone 3G和模拟器中显示的文本? 为什么在iPhone 4中总是显示正确的文本?

对我来说,它看起来好像在同一个地方有两个button,一个在另一个之上。 在iPhone 4中,“我的”自定义button位于前面,隐藏了旧的系统生成的button,但在模拟器和iPhone 3G中,旧的系统生成button位于前面,隐藏了我的自定义button。 但是:即使我隐藏的自定义button比系统生成的更大(更宽),也没有任何可见的。 只有当滑出animation开始时,我的button才可见。


编辑: 2012-04-07 16:38 +02:00(中欧夏令时)

接下来有趣的事实

这是到现在为止发生的事情:

当button第一次出现时(第二张截图,见下文),我把一个字作为标题,这对于从系统中将会变成的字来说是相同的。 然后用户select一些动作,这个button被另一个视图隐藏。 在另一个用户动作之后,button被再次显示出来,现在它会得到一个新的词作为标题(意思相同,但是新语言),但是在iPhone 3G和模拟器上,旧标题是“更强”的。 新的标题将不会显示。 旧的标题粘在那里。

如果在第一次出现的时候,我不会在button上写上一个字作为标题,这与系统生成的标题不同。 如果第一个标题与默认标题不同,稍后的更改将在所有iPhone和模拟器上执行。

这让我相信,iOS会做一些“优化”:如果在button初次出现时,自定义标题与系统生成的标题相同,那么稍后更改button标题将被忽略,但仅在iPhone上3G和模拟器。 在iPhone 4上,以后的更改将被允许在任何情况下。

但是,在开始时设置不同的标题以防止应用程序出现错误的行为不是一个选项。

我怀疑你所看到的问题是在模拟器上发生的序列和真实的硬件之间微妙的时间问题。

视图控件不一定在viewDidLoad中实例化,所以你应该等到viewWillAppear设置标题值(等)。

以下意思是build设性的,请按照其意图的精神来采取:

没有详细的审查你的代码,我怀疑你正在努力达到的目标可以更确定地实现。 你所要做的并不难,也不寻常,但恐怕你的代码看起来不必要的复杂 – 可能是因为你试图解决这些时间问题。

看看一些简单的例子和​​教程,并看看简化你的代码,所以它不使用标志来跟踪状态(mustRedrawMyself),因为这不应该是必要的。 请记住,不要设置视图/控件的属性,直到viewWillAppear,看看你如何继续。

如果你还没有,那么你可能还想看看对本地化的内置支持。

祝你好运。

我会试试这个:

将自定义折叠(如果和执行)移动到您要在其viewWillAppear中更新到本地化的位置:

在更改标题之前,您正在向REDRAW讲述视图,因此标题更新不是重绘的一部分。 它正在做什么它应该..我的猜测是在某些硬件/仿真器上的计时操作更快,所以在重绘调用之后进行的更新发生在某些硬件/仿真器上的绘制完成之前,但不会在平局发生在其他。

尝试改变操作的顺序以下,让我知道如果这是行之有效的。

 - (void) viewWillAppear:(BOOL)animated { // this is an important method! Maybe here is the reason for // my problem! [super viewWillAppear:animated]; // And here comes the buggy back button: // When ever this controllers view is going to appear, // a new back button will be created with a title in the // new language: UIBarButtonItem* BB = [[UIBarButtonItem alloc] init]; BB.title = LocalizedString(MYTITLE); self.myBackButton = BB; self.navigationItem.backBarButtonItem = self.myBackButton; // When ever this controllers view is going to appear, and // when ever it is necessary to redraw it in a new language, // it will redraw itself: if (self.mustRedrawMyself) { [self redraw]; } } 

苹果支持确实回答

我联系了苹果支持这个问题,他们回答了。

问题是,导航栏保存导航控制器堆栈上的视图的所有后退button,并且所有这些button都需要同时更新。 在viewWillAppear-Methods中更新堆栈中的视图是很好的,但试图在这个地方更新后退button并不是一个好主意。


解决scheme:

扩展UIViewController的接口:

 @interface UIViewController (extended) - (NSString *)localizedKey; @end 

对于在UINavigationController的堆栈上放置一个视图的每个UIViewController都实现这个方法:

 - (NSString*) localizedKey { return @"a title-keyword"; } 

不要在任何UIViewControllers中UIBarButtonItemself.navigationItem.backBarButtonItem

如果需要更改标题,请使用代码片段对所有后退button执行此操作(请记住: LocalizedString(key)是一个类似于NSLocalizedString(key,comment)的自写macros):

 NSArray* vcs = [self.navigationController viewControllers]; for (UIViewController* vc in vcs) { vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]); vc.title = LocalizedString([vc localizedKey]); } 

苹果支持的逐字回答:

我们正在与导航栏战斗,在错误的时间强制更新。 注意每个视图控制器中的所有视图都会得到正确更新。 所以导航栏需要特别注意才能得到我们想要的。

为了达到这个目的,你需要立即将后退button更改为堆栈中的所有视图控制器(当用户select一种语言时),而不是通过“viewWillAppear”来显示它们。

这需要能够以公开的方式获得该button的本地化密钥。 我向UIViewController引入了一个类来容易地采用这个:

@interface UIViewController(扩展)
– (NSString *)localizedKey;
@结束

然后你的LangSelectTabVC类可以一次改变所有后退button。 这种方法将使button标题重绘正确。

所以在viewWillAppear中,你不必更新每个后退button。 在UIKit中做这件事似乎已经太迟了,无法赶上这个更新。 发生更新时,您也重新创build一个新的后退button。 这是没有必要的,只要采取当前的一个,并改变它的标题:

NSArray * vcs = [self.navigationController viewControllers];
for(UIViewController * vc in vcs)
{
vc.navigationItem.backBarButtonItem.title = LocalizedString([vc localizedKey]);
vc.title = LocalizedString([vc localizedKey]);
}

我附加了一个显示此解决方法的修改后的项目。