Приложение ведет себя по-разному на симуляторе iPhone 5.1 и реальном iPhone 4 с iOS 5.1

In a nutshell:

Когда заголовок кнопки возврата навигационного контроллера изменяется, в некоторых случаях старый заголовок застревает, а новый заголовок не отображается. Это происходит только в некоторых воспроизводимых ситуациях, в то время как это работает, как задумано в других ситуациях.

It depends on the hardware

The error happens on iPhone 3G (iOS 4.2.1) and in the simulator (iOS 5.1) With identical sourcecode there is no error on iPhone 4 (iOS 5.1)

It depends on the word that is written to the title

When the button is created, and it gets from my selfwritten creating-method the same word as title that it would have got automatically (i.e. the title of the previous page on the navigation controller's stack), and when the other circumstances match, then, when trying to change the button's title at a later moment, the old text is stuck and the new title will not show up. When at creation time the button gets a word as title that is different from its default-title, then every later changes of its title works fine, as long as you don't assign the default-title to it. If, after a lot of successfull changes with many different titles, you put the word on the buttons title, that was its default title, then this word is stuck. Later changes will not be accepted (without any message, and only if the other circumstances match)

It depends on whether the button was invisible in the meantime or not.

If another view was pushed on the navigation controllers stack, so that the old page with the flawed button became hidden by the new page, and when the new page was popped from the stack later again which makes the button visible again, (and when the other circumstances match) then the old text was stuck and the trial to change it is ignored (without any message). If the button was newer hidden, changing its title never is no problem. I works always.

The correct title is visible during animation

When the attempt to change the back-button's title was ignored due to the combination of the circumstances described above, the proper title anyhow becomes visible for about 0.3 seconds when this back-button is hit and the page's slide-to-right-animation is processed. At the beginning of the animation the old stuck title is replaced by the proper title, and the correct title is visible during the animation. Detailed description

Речь идет о тексте наUINavigationControllerкнопку "назад". Я меняю название этой кнопки в зависимости от новых настроек языка. На данный момент мое приложение имеет максимум 3 контроллера представления в стеке навигационных контроллеров. Каждый из них является отдельным подклассом `UITableViewController.

Таблица 1, названнаяGeneralTableVC является корневым представлением в стеке. У него нет кнопки назад. Он дает пользователю сводку того, что он сохранил в приложении, и отображает панель инструментов с кнопкой настроек.

Именно контроллер навигации предоставляет эту панель инструментов, которая видна в таблице 1. Она установлена как невидимая в таблицах 2 и 3. На данный момент на этой панели инструментов есть только одна кнопка с именем & quot; Настройки & quot ;. Если дотронуться до этой кнопки настроек, таблица 2 будет помещена в стек.

Таблица 2, названнаяSettingsTabVC имеет кнопку «назад», и именно она создает проблемы в симуляторе, но отлично работает на моем настоящем iPhone 4 под управлением iOS 5.1.

При прикосновении к первой строке таблицы 2 будет создана новая таблица (таблица 3), которая будет помещена в стек.

Таблица 3, названнаяLangSelectTableVC Также есть кнопка «Назад», но она отлично работает на обоих устройствах, симуляторе iPhone и реальном iPhone 4.

Таблица 3 - это таблица выбора языка, которая отображает список всех доступных языков (на данный момент только английский и немецкий). Прикосновение к строке изменяет настройки немедленно. Активное представление (Таблица 3) будет перерисовано, и в течение нескольких миллисекунд все тексты на экране появятся на новом языке.

Перерисовка самой таблицы не составляет проблем, так же как и заголовок на панели навигации. Но текст на кнопке «Назад» тоже должен быть переведен, и это немного сложно. Я проделал один и тот же трюк с обеими кнопками назад, и он отлично работает для кнопки, видимой в Таблице 3, которая указывает на Таблицу 2. Но с тем же кодом есть проблема в симуляторе (но не на реальном iPhone) с кнопкой на Таблице 2, которая указывает на Таблицу 1.

Я даю вам несколько фрагментов кода и несколько скриншотов, чтобы показать вам, что я сделал и что происходит:

Sourcecode

ARC (automatic reference counting) is in use.

Я определил протокол перерисовки:

Protocols.h

<code>#ifndef ToDo_Project_Protocols_h
#define ToDo_Project_Protocols_h

@protocol redrawProt
- (void) mustRedraw;
@end

#endif
</code>

Это заголовок таблицы 1:

GeneralTableVC.h

<code>#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
</code>

Заголовочные файлы других таблиц,SettingsTabVC.h а такжеLangSelectTabVC.h определить одинаковые свойства и идентичную функцию init

Программа начинается здесь:

part of AppDelegate.m

<code>- (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
}
</code>

Далее идет реализация таблицы 1 (GeneralTableVC.m). Код в таблице 2 (SettingsTabVC.m) и таблица 3 (LangSelectTabVC.m) аналогично тому же. Я не показываю те части кода, которые реализуют протокол UITableViewDataSource. Я думаю, что эти части не очень важны для объяснения проблемы.

В этом коде вы найдете макросLocalizedString(keyword) который делает точно так же, какNSLocalizedString(keyword,comment), который переводит ключевое слово на нужный язык. Моя версия этого макроса использует другой бундель для перевода (не основной пакет)

GeneralTableVC.m

<code>#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
</code>
Screenshots

При запуске приложения на iPhone 5.1 симулятор поставит Таблицу 1 (GeneralTableVC) на экране:

after starting the app

На панели инструментов на кнопке экранов, с правой стороны, вы найдете кнопку настроек. При нажатии на эту кнопку на экране появляется следующая таблица:

settings-screen

Смотрите кнопку назад в строке заголовка. Он отображает текст «Сводка», который является правильным, поскольку предыдущий заголовок таблицы был «Сводка».

Теперь перейдем к первой строке (& quot;Language English >& Quot;):

before changing language

Все отлично. Теперь давайте изменим язык. Вкладка & quot;German& Quot ;:

after changing language

Вот Это Да! Теперь все по-немецки. Даже кнопка «Назад» изменилась с «Настройки» на "Einstellungen".

Давайте перейдем на вкладку «Einstellungen» Кнопка назад:

back button has wrong language in simulator

Теперь почти все в порядке; все изменилось на немецкий. Все, кроме кнопки «Назад», которая по-прежнему гласит «Сводка» вместо & quot; & xDC; berblick & quot ;. И я не понимаю почему, потому что, когда я делаю точно такие же шаги с точно таким же исходным кодом на моем реальном iPhone 4, последний экран выглядит так:

correct langugage on real iPhone

Следите за текстом на кнопке «Назад». На настоящем iPhone 4 это немецкое слово & quot; berblick & quot; (это то, что я хочу), но в симуляторе это английское слово & quot; Summary & quot ;. И для меня это означает, что на некоторых телефонах (например, на моем iPhone 4) пользователь получает то, что ожидается, но, возможно, на некоторых других телефонах (может быть, iPhone 4S) пользователь получает глючный дисплей.

Кто-нибудь знает, что не так с моим кодом?

EDITs

Edit: 2012-04-06 09:04 +02: 00 (центральноевропейское летнее время)

Мне удалось протестировать мое приложение на другом оборудовании, старом iPhone 3G (iOS 4.2.1). На старом iPhone мое приложение работает точно так же, как в симуляторе. Запуск одного и того же приложения на iPhone 4 приводит к другому поведению.

Чтобы быть более точным:

On iPhone 4 (iOS 5.1): App is doing what I want, no faulty behavior. On Simulator (iOS 5.1): App displays wrong title on a navigation controllers back button. On iPhone 3G (iOS 4.2.1): App shows the same faulty behaviour as in the simulator.

Edit: 2012-04-07 10:14 +02: 00 (центральноевропейское летнее время)

Наблюдая за переходом на iPhone 3G, я обнаружил кое-что интересное и, возможно, полезное: когда я нажимаю на кнопку с неправильным текстом, происходит следующее:

The wrong text is replaced by the correct text After this replacement the view dissapears animated (sliding to the right) and the underlaying view becomes visible. This transition has a duration of aproximately 0.3 seconds, and in this short interval the correct text is visible in all hardware-iPhones and in the simulator too.

Но все еще остается вопрос: почему внешний текст отображается в iPhone 3G и Simulator? Почему в iPhone 4 всегда отображается правильный текст?

Для меня это выглядит так, как будто в одном месте было две кнопки, одна над другой. В iPhone 4 «мой» Пользовательская кнопка находится впереди, скрывая старую сгенерированную системой кнопку, но в симуляторе и в iPhone 3G старая сгенерированная системой кнопка находится спереди, скрывая мою пользовательскую кнопку. Но: даже если моя скрытая пользовательская кнопка больше (шире), чем сгенерированная системой, ничего из этого не видно. Только когда начинается слайд-анимация, моя кнопка становится видимой.

Edit: 2012-04-07 16:38 +02: 00 (центральноевропейское летнее время)

Следующий интересный факт:

Вот что произошло до сих пор:

Когда кнопка нажимается в первый раз (2-й снимок экрана, см. Ниже), я добавляю к ней слово в качестве заголовка, которое идентично слову, которое было бы раньше в системе. Затем пользователь выбирает какое-то действие, и эта кнопка скрыта другим представлением. После другого пользовательского действия кнопка снова открывается, теперь она получит новое слово в качестве заголовка (то же самое значение, но новый язык), но на iPhone 3G и на симуляторе старый заголовок «сильнее». Новый заголовок не будет отображаться. Старый заголовок остается там.

Этого не произойдет, если при первом появлении я напишу слово как заголовок на кнопку, которое отличается от сгенерированного системой заголовка. Если первый заголовок отличается от заголовка по умолчанию, более позднее изменение будет выполнено на всех iPhone и на симуляторе.

Это заставляет меня поверить, что iOS выполняет некоторую «оптимизацию»: если при первом появлении кнопки пользовательский заголовок идентичен сгенерированному системой заголовку, то более позднее изменение заголовка кнопок будет игнорироваться, но только при iPhone 3G и симулятор. На iPhone 4 более поздние изменения будут разрешены в любом случае.

Но установка другого заголовка в начале, чтобы предотвратить неправильное поведение приложения, не подходит.

Ответы на вопрос(3)

Ваш ответ на вопрос