UINavigationBar с UISegmentedControl частично покрывает дочерние представления

Я прочитал много других тем по этому вопросу и в документации Apple, но еще не нашел решения для своей конкретной проблемы.

Мое приложение использует UITabBarController в качестве rootViewController, а на одной из вкладок у меня есть UISegmentedControl в navigationBar для переключения между тремя дочерними UITableViewController.

(В реальном приложении два дочерних VC являются пользовательскими UIViewController, я просто использую три UITableViewController для примера приложения).

Настройка segmentedControl и переключение работают нормально. Что идет не так, так это то, что правильно отображается только первый UITableViewController. Для второго и третьего часть первой ячейки скрыта под navigationBar. Когда я щелкаю по всем трем, первый все еще в порядке.

Я сделал небольшой пример приложения, чтобы показать, что происходит, используя очень яркие цвета для демонстрационных целей: https://www.dropbox.com/s/7pfutvn5jba6rva/SegmentedControlVC.zip?dl=0

Вот также код (я не использую раскадровки):

// AppDelegate.m

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    // Override point for customization after application launch.
    FirstViewController *fvc = [[FirstViewController alloc] init];
    UINavigationController *firstNavigationController = [[UINavigationController alloc] initWithRootViewController: fvc];

    SecondViewController *svc = [[SecondViewController alloc] init];
    UINavigationController *secondNavigationController = [[UINavigationController alloc] initWithRootViewController: svc];

    // Initialize tab bar controller, add tabs controllers
    UITabBarController *tabBarController = [[UITabBarController alloc] init];
    tabBarController.viewControllers = @[firstNavigationController, secondNavigationController];

    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = tabBarController;
    [self.window makeKeyAndVisible];

    return YES;
}


// FirstViewController.m

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.

    self.title = @"One";
    self.view.backgroundColor = [UIColor orangeColor];

    UITableViewController *vc1 = [[UITableViewController alloc] init];
    UITableViewController *vc2 = [[UITableViewController alloc] init];
    UITableViewController *vc3 = [[UITableViewController alloc] init];

    vc1.view.backgroundColor = [UIColor redColor];
    vc2.view.backgroundColor = [UIColor blueColor];
    vc3.view.backgroundColor = [UIColor greenColor];

    self.viewControllers = @[vc1, vc2, vc3];
    self.segmentTitles = @[@"Red", @"Blue", @"Green"];

    self.segmentedControl = [[UISegmentedControl alloc] initWithItems: self.segmentTitles];
    [self.segmentedControl addTarget: self
                              action: @selector(segmentClicked:)
                    forControlEvents: UIControlEventValueChanged];

    self.navigationItem.titleView = self.segmentedControl;

    self.segmentedControl.selectedSegmentIndex = 0;

 // set the first child vc:  
    UIViewController *vc = self.viewControllers[0];

    [self addChildViewController: vc];
    vc.view.frame = self.view.bounds;
    [self.view addSubview: vc.view];
    self.currentVC = vc;
}

- (void)segmentClicked:(id)sender
{
    if (sender == self.segmentedControl)
    {
        NSUInteger index = self.segmentedControl.selectedSegmentIndex;
        [self loadViewController: self.viewControllers[index]];
    }
}

- (void)loadViewController:(UIViewController *)vc
{
    [self addChildViewController: vc];

    [self transitionFromViewController: self.currentVC
                      toViewController: vc
                              duration: 1.0
                               options: UIViewAnimationOptionTransitionFlipFromBottom
                            animations: ^{
                                [self.currentVC.view removeFromSuperview];
                                vc.view.frame = self.view.bounds;
                                [self.view addSubview: vc.view];
                            } completion: ^(BOOL finished) {
                                [vc didMoveToParentViewController: self];
                                [self.currentVC removeFromParentViewController];
                                self.currentVC = vc;
                            }
     ];
}

Поэтому, очевидно, мой вопрос: почему это происходит и что я могу сделать, чтобы это исправить?

Редактировать: добавление скриншотов.

Первый виртуальный инвестор Второй ВК Третий VC

РЕДАКТИРОВАТЬ: Основываясь на ответе ниже, я изменил код в блоке анимации на:

[self.currentVC.view removeFromSuperview];

if ([vc.view isKindOfClass: [UIScrollView class]])
{
    UIEdgeInsets edgeInsets = UIEdgeInsetsMake(self.topLayoutGuide.length, 0, self.bottomLayoutGuide.length, 0);
    [UIView performWithoutAnimation: ^{
      vc.view.frame = self.view.bounds;
       ((UIScrollView *)vc.view).contentInset = edgeInsets;
         ((UIScrollView *)vc.view).scrollIndicatorInsets = edgeInsets;
     }];
  }
   else
   {
       vc.view.frame = self.view.bounds;
   }

   [self.view addSubview: vc.view];

Теперь это работает. Я также собираюсь попробовать это с пользовательским UIViewController.


person koen    schedule 01.08.2015    source источник
comment
Выложите скриншоты проблемы.   -  person Leo Natan    schedule 01.08.2015
comment
Я добавил скриншоты, вы можете видеть, что разделительные строки 2-й и 3-й таблицы выше.   -  person koen    schedule 01.08.2015
comment
Не рассчитывайте вставку вручную. Вместо этого используйте topLayoutGuide.length контроллера.   -  person Leo Natan    schedule 01.08.2015
comment
Что касается анимации, вы можете использовать [UIView performWithoutAnimation:] для установки вставок без анимации.   -  person Leo Natan    schedule 01.08.2015
comment
Хм, если я использую topLayoutGuide.length, то viewDidLayoutSubviews вызывается только в первый раз (для красного ВК), и моя проблема возвращается.   -  person koen    schedule 01.08.2015
comment
Что-то странное. viewDidLayoutSubviews не следует использовать для установки вставок последующих представлений, потому что его действительно не следует вызывать позже. А вы говорите, установка их в loadViewController: не работает, что странно. Если вы установили их только в loadViewController: и видите плохо, что произойдет, если вы попытаетесь прокрутить пальцем на плохом виде таблицы? Позволяет ли он прокручивать ячейку в полноэкранном режиме? Если это так, вам также нужно установить contentOffset равным {0, contentInset.top}.   -  person Leo Natan    schedule 01.08.2015
comment
Ах, я неправильно понял, я переместил код вставки в блок анимации, и теперь topLayoutGuide.length работает. Мне пришлось еще немного подправить код в блоке анимации, чтобы заставить его работать, см. мое редактирование выше.   -  person koen    schedule 01.08.2015


Ответы (1)


Проблема в том, что вы не устанавливаете правильную вставку содержимого для каждого табличного представления. Система пытается сделать это за вас, но я предполагаю, что ваша настройка слишком сложна для нее, и она делает это только для первого табличного представления, загруженного в viewDidLoad. В вашем методе loadViewController: при замене текущего отображаемого представления обязательно установите для contentInset и scrollIndicatorInsets значения предыдущего представления. Я думаю, система сможет установить правильные вставки позже, если вы повернетесь в альбомную ориентацию. Попытайся. Если это не так, вам нужно будет сделать это самостоятельно в viewDidLayoutSubviews.

person Leo Natan    schedule 01.08.2015
comment
Установка contentInset и scrollIndicatorInsets не сработала. Я собираюсь изучить viewDidLayoutSubviews. Но для этого потребуется создать подкласс UITableViewController, правильно? - person koen; 01.08.2015
comment
Нет, я имел в виду в контроллере контейнера. - person Leo Natan; 01.08.2015