IOS7 - Установка selectedIndex UITabBarController разбивает события касания вдоль правого края экрана?

Я поразил странную проблему с UITabBarController на iOS7 и не могу найти способ обхода, поэтому любая помощь будет приветствоваться!

Сценарий:

  • Навигационное приложение, использующее ландшафтную ориентацию на iPad.
  • Приложение состоит из основного вида и второго представления, которое является UITabBarController.
  • TabBarController имеет две вкладки.
  • В первом представлении есть две кнопки - каждая кнопка выполняет переход к контроллеру панели вкладок и устанавливает другую вкладку как выбранную. (т.е. кнопка1 выбирает первую вкладку, а кнопка2 выбирает вторую вкладку).
  • Настройка вкладки выполняется в prepareForSegue, вызывая setSelectedIndex в контроллере панели вкладок.

Simple storyboard that demonstrates the issue

Результат:

В iOS 7 я обнаружил, что представление, показанное в контроллере панели вкладок, не регистрирует никаких событий касания вдоль правого края представления! Таким образом, в раскадровке, показанной выше, UISwitch в правой части экрана не может быть задействован.

Я даже прикреплял распознаватель жестов к представлениям и использовал его для регистрации области экрана, на которую можно было коснуться - он, похоже, регистрирует события касания примерно до x = 770 точек в поперечнике. Остальные 1/4 экрана "неприкасаемые"!

После сеанса, если вы вручную переключитесь на другую вкладку и снова вернетесь, события касания будут "фиксированными", а полный вид снова будет реагировать на касания.

Это не похоже на проблему на iOS 5/6.

Любая помощь, которую очень ценят:

  • Что вызывает это в первую очередь (ошибка/изменение iOS7?)
  • Как еще я могу обойти это? Я попытался позвонить setSelectedViewController, а также использовать setSelectedIndex, и это похоже на то же самое.

Спасибо заранее.

Ответы

Ответ 1

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

Вид контейнера, который настраивается контроллером панели табуляции, чтобы содержать ваш контроллер просмотра, не изменяется, чтобы учесть, что интерфейс находится в альбомной ориентации. Его размеры в то время, когда отображается ваш контроллер вида, составляют 768 (ширина) x 1024 (высота).

Иерархия представлений выглядит так, когда отображается выбранное представление табуляции:

UIWindow
    /* Navigation Controller */
    UILayoutContainerView
        UINavigationTransitionView
            UIViewControllerWrapperView
                /* Tab bar controller */
                UILayoutContainerView
                    UITransitionView
                        UIViewControllerWrapperView <-- Incorrectly sized.
                            /* MyViewController */
                            MyViewController.view

Неверный размер UIViewControllerWrapperView не вызывает проблемы с отображением, потому что subviews все еще отображаются, даже если они находятся за пределами границ надзора. Однако маршрутизация событий намного более строгая. События в правой четверти экрана никогда не маршрутизируются в представлении диспетчера просмотров, потому что тест с ошибкой выходит из строя в UIViewControllerWrapperView с неправильным размером, где событие выходит за пределы UIViewControllerWrapperView.

В качестве обходного пути я подклассифицировал UITabBarController и добавил в viewWillAppear следующее:

@implementation FixedIOS7TabBarController

- (void)viewWillAppear:(BOOL)animated {
    [super viewWillAppear:animated];
    // Fix the frame of the UIViewControllerWrapperView
    self.selectedViewController.view.superview.frame = self.view.bounds;
}

@end

Надеюсь, что это поможет кому-то еще...

Ответ 2

Как объяснено в этом ответе,

Вид контейнера, который настраивает контроллер панели вкладок, чтобы содержать контроллер просмотра не изменяется для учета интерфейса находясь в ландшафтной ориентации. Его размеры во время вашего просмотра отображается контроллер 768 (ширина) x 1024 (высота).

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

Решение, предложенное в этом ответе, не работает для меня, потому что viewWillAppear: вызывается только один раз. Тем не менее, viewDidLayoutSubvews вызывается всякий раз, когда изменяется вид, включая поворот, поэтому мое решение заключалось в подклассе UITabBarController и выполнении обходного пути в viewDidLayoutSubvews:

@implementation FixedIOS7TabBarController

- (void)viewDidLayoutSubviews
{
    // fix for iOS7 bug in UITabBarController
    self.selectedViewController.view.superview.frame = self.view.bounds;
}

@end

Ответ 3

В конце найдите обходное решение здесь:

self.view.autoresizesSubviews = YES;
self.view.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;

Ответ 4

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

Итак, я создаю свое собственное решение, я не уверен, что это нормальное решение.

@implementation FixedIOS7TabBarController
- (UIView*)findInSubview:(UIView*)view className:(NSString*)className
{
    for(UIView* v in view.subviews){
        if([NSStringFromClass(v.class) isEqualToString:className])
            return v;
        UIView* finded = [self findInSubview:v className:className];
        if(finded)
            return finded;
    }
    return nil;
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    UIView* wraperView = [self findInSubview:self.view className:@"UIViewControllerWrapperView"];
    wraperView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
}
@end

Прекрасно работает для меня!

Ответ 5

В списке контроллеров представления в левой части экрана найдите обработчики вида/представления, перетащите представление под первым ответчиком, чтобы он не был связан с представлением контроллера вида.

Затем перейдите на вкладку макета с правой стороны, выберите все 4 анкера и оба набора стрелок изменения размера (горизонтально + вертикально).

Затем перетащите представление обратно туда, где оно было первоначально (чуть ниже контроллера вида).