Подтвердить что ты не робот

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

В двух словах:

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

Это зависит от аппаратного обеспечения

  • Ошибка происходит на iPhone 3G (iOS 4.2.1) и в симуляторе (iOS 5.1)
  • При идентичном исходном коде нет ошибки на iPhone 4 (iOS 5.1)

Это зависит от слова, которое записывается в заголовок

  • Когда кнопка создана, и она получает от моего самонастраиваемого метода создания то же слово, что и название, которое оно получило бы автоматически (т.е. заголовок предыдущей страницы в стеке контроллера навигации), и когда совпадают другие обстоятельства, то при попытке изменить название кнопки в более поздний момент старый текст застревает, и новый заголовок не появится.
  • Когда во время создания кнопка получает слово как название, отличное от его названия по умолчанию, тогда все последующие изменения его названия работают нормально, если вы не назначаете ему по умолчанию заголовок.
  • Если после много успешных изменений со многими разными названиями вы помещаете слово на заголовок кнопок, это было его название по умолчанию, то это слово застряло. Более поздние изменения не будут приняты (без какого-либо сообщения, и только если совпадают другие обстоятельства)

Это зависит от того, была ли кнопка невидимой тем временем или нет.

  • Если в стеке навигационных контроллеров было добавлено другое представление, так что старая страница с ошибочной кнопкой стала скрыта новой страницей, а после этого новая страница снова выскочила из стека, что снова отобразит кнопку ( и когда совпадают другие обстоятельства), тогда старый текст застрял, и пробная версия для его изменения игнорируется (без какого-либо сообщения).
  • Если кнопка была более скрытой, изменение ее названия никогда не будет проблемой. Я всегда работаю.

Правильный заголовок отображается во время анимации

  • Когда попытка изменить заголовок задней кнопки была проигнорирована из-за сочетания описанных выше обстоятельств, надлежащее название становится видимым примерно на 0,3 секунды, когда эта кнопка включена, а страница слайд-право -анимация обрабатывается. В начале анимации старый застрявший заголовок заменяется соответствующим заголовком, а правильный заголовок отображается во время анимации.

Подробное описание

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

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

Это контроллер навигации, который предоставляет эту панель инструментов, которая видна в Таблице 1. В таблице 2 и 3. она установлена ​​в невидимую. На данный момент в этой панели инструментов с именем "Настройки" есть только одна кнопка. Прикосновение к этой кнопке настроек приведет к тому, что таблица 2 будет перенесена в стек.

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

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

В таблице 3, названной LangSelectTableVC, также есть кнопка "назад", но эта работает очень хорошо в обоих устройствах, симуляторе iPhone и реальном iPhone 4.

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

Повторная перерисовывание самой таблицы не проблема, а также заголовок в панели навигации. Но текст на обратной кнопке тоже должен быть переведен, и это немного сложно. Я сделал тот же трюк на обеих кнопках, и он отлично работает для кнопки, видимой в таблице 3, которая направляется в Таблицу 2. Но с тем же кодом есть проблема в симуляторе (но не на реальном iPhone) с кнопкой в ​​таблице 2, которая направляется в таблицу 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.h и LangSelectTabVC.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. Я думаю, что эти части не очень важны для объяснения проблемы.

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

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):

after starting the app

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

settings-screen

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

Теперь мы вставляем в первую строку ( "Language English >" ):

before changing language

Все в порядке. Теперь измените язык. Вкладка "German":

after changing language

Ничего себе! Сейчас все на немецком языке. Даже кнопка "Назад" изменилась с "Настройки" на "Einstellungen".

Позволяет включить вкладку "Einstellungen" назад:

back button has wrong language in simulator

Сейчас все в порядке; все изменилось на немецкий. Все, кроме кнопки "Назад" , которая по-прежнему говорит "Сводка" вместо "Уберблик". И я не понимаю, почему, потому что, когда я делаю точно такие же шаги с точно таким же исходным кодом на моем реальном iPhone 4, последний экран выглядит следующим образом:

correct langugage on real iPhone

Обратите внимание на текст на задней кнопке. На реальном iPhone 4 это немецкое слово "Überblick" (это то, что я хочу), но в симуляторе это английское слово "Summary". И это означает, что на некоторых телефонах (например, на iPhone 4) пользователь получает то, что ожидается, но, возможно, на некоторых других телефонах (возможно, iPhone 4S) пользователь получает багги-дисплей.

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


редактирует

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

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

Точнее:

  • На iPhone 4 (iOS 5.1): приложение делает то, что я хочу, не ошибочное поведение.
  • В Simulator (iOS 5.1): приложение отображает неправильный заголовок на кнопке обратной связи навигационных контроллеров.
  • В iPhone 3G (iOS 4.2.1): приложение показывает те же ошибки, что и в симуляторе.

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

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

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

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

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


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

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

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

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

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

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

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

4b9b3361

Ответ 1

Поддержка Apple ответила

Я связался с поддержкой Apple для этой проблемы, и они ответили.

Проблема заключалась в том, что на панели навигации хранятся все обратные кнопки для представлений в стеке контроллеров навигации, и все эти кнопки необходимо обновлять одновременно. Обновление представлений, которые лежат в стеке в их viewWillAppear-Методах, хорошо, но попытка обновить обратную кнопку в этом месте не является хорошей идеей.


Решение:

Расширьте интерфейс UIViewController:

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

Для каждого UIViewController, который помещает представление в стек UINavigationController, реализует этот метод:

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

Не вмешивайтесь в UIBarButtonItem или self.navigationItem.backBarButtonItem в любом из UIViewControllers.

Если при этом нужно изменить названия, сделайте это для ВСЕХ кнопок с этим фрагментом кода (помните: LocalizedString(key) - это самозаписывающийся макрос, похожий на NSLocalizedString(key,comment)):

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

Вербальный ответ поддержки Apple:

Мы боремся с панелью навигации, вызывая обновление в неподходящее время. Обратите внимание, что все представления в каждом контроллере просмотра обновляются соответствующим образом. Поэтому навигационная панель требует особого внимания, чтобы получить то, что мы хотим.

Чтобы это сработало, вам нужно сразу же изменить обратные кнопки на все контроллеры представления в стеке (в то время, когда пользователь выбирает язык), а не когда каждый из них появляется через "viewWillAppear".

Для этого требуется возможность получить локализованный ключ для этой кнопки общедоступным способом. Я ввел категорию в UIViewController, чтобы легко принять это:

@interface UIViewController (расширенный)
- (NSString *) localizedKey; @end

Затем ваш класс LangSelectTabVC может сразу изменить все обратные кнопки. Этот подход позволит правильно перерисовать названия кнопок.

Итак, в viewWillAppear вам не придется обновлять каждую кнопку возврата. Выполнение этого, похоже, слишком поздно для UIKit, чтобы поймать это обновление. При обновлении вы также заново создаете новую кнопку возврата. Это необязательно, просто возьмите текущий и измените заголовок:

NSArray * vcs = [self.navigationController viewControllers];
для (UIViewController * vc в vcs)
{
    vc.navigationItem.backBarButtonItem.title= LocalizedString ([vc localizedKey]);
    vc.title= LocalizedString ([vc localizedKey]); }

Я приложил модифицированный проект, показывающий это обходное решение.

Ответ 2

Я подозреваю, что проблемы, которые вы видите, сводятся к тонким вопросам синхронизации между последовательностью, которая возникает на Симуляторе, и реальным оборудованием.

Элементы управления представлением не обязательно создаются в viewDidLoad, поэтому вы должны дождаться, когда viewWillAppear установит значения заголовка (и т.д.).

Следующее подразумевается конструктивно, пожалуйста, возьмите его в духе, который он намеревался:

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

Взгляните на некоторые простые примеры и учебные пособия и попробуйте упростить свой код, чтобы он не использовал флаги для отслеживания состояния (mustRedrawMyself), поскольку это не обязательно. Не забудьте не устанавливать свойства представлений/элементов управления до viewWillAppear и посмотреть, как вы это делаете.

Вы также можете посмотреть встроенную поддержку локализации, если вы еще этого не сделали.

Удачи.

Ответ 3

Я бы попробовал это:

Переместите SELF REDRAW, если и выполняйте его, ПОСЛЕ того, где вы обновляете его, в свою локализацию в вашем представленииWillAppear:

Вы передаете представление 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];   
}  

}