Содержимое попадает под панель навигации при внедрении в пользовательский контроллер представления контейнера.



обновление



основываясь на ответе Тима, я реализовал следующее В каждом контроллере представления, который имел scrollview (или подкласс), который был частью моего пользовательского контейнера:



- (void)didMoveToParentViewController:(UIViewController *)parent
{
if (parent) {
CGFloat top = parent.topLayoutGuide.length;
CGFloat bottom = parent.bottomLayoutGuide.length;

// this is the most important part here, because the first view controller added
// never had the layout issue, it was always the second. if we applied these
// edge insets to the first view controller, then it would lay out incorrectly.
// first detect if it's laid out correctly with the following condition, and if
// not, manually make the adjustments since it seems like UIKit is failing to do so
if (self.collectionView.contentInset.top != top) {
UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0);
self.collectionView.contentInset = newInsets;
self.collectionView.scrollIndicatorInsets = newInsets;
}
}

[super didMoveToParentViewController:parent];
}


~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~



у меня есть пользовательский контроллер представления контейнера под названием SegmentedPageViewController. Я установил это как UINavigationController's rootViewController.



цель SegmentedPageViewController, чтобы UISegmentedControl, установить в качестве titleView NavController, чтобы переключаться между различными контроллеры дочерних представлений.



enter image description here



все эти дочерние контроллеры представления содержат представление scrollview, tableview или collection.



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



enter image description here



мы используем auto layout и interface builder. Мы перепробовали все, что могли придумать, но не можем найти последовательного решения.



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



- (void)switchFromViewController:(UIViewController *)oldVC toViewController:(UIViewController *)newVC
{
if (newVC == oldVC) return;

// Check the newVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (newVC) {

// Set the new view controller frame (in this case to be the size of the available screen bounds)
// Calulate any other frame animations here (e.g. for the oldVC)
newVC.view.frame = self.view.bounds;

// Check the oldVC is non-nil otherwise expect a crash: NSInvalidArgumentException
if (oldVC) {
// **** THIS RUNS WHEN A NEW VC IS SET ****
// DIFFERENT FROM FIRST VC IN THAT WE TRANSITION INSTEAD OF JUST SETTING


// Start both the view controller transitions
[oldVC willMoveToParentViewController:nil];
[self addChildViewController:newVC];

// Swap the view controllers
// No frame animations in this code but these would go in the animations block
[self transitionFromViewController:oldVC
toViewController:newVC
duration:0.25
options:UIViewAnimationOptionLayoutSubviews
animations:^{}
completion:^(BOOL finished) {
// Finish both the view controller transitions
[oldVC removeFromParentViewController];
[newVC didMoveToParentViewController:self];
// Store a reference to the current controller
self.currentViewController = newVC;
}];
} else {

// **** THIS RUNS WHEN THE FIRST VC IS SET ****
// JUST STANDARD VIEW CONTROLLER CONTAINMENT

// Otherwise we are adding a view controller for the first time
// Start the view controller transition
[self addChildViewController:newVC];

// Add the new view controller view to the view hierarchy
[self.view addSubview:newVC.view];

// End the view controller transition
[newVC didMoveToParentViewController:self];

// Store a reference to the current controller
self.currentViewController = newVC;
}
}

}
524   5  

5 ответов:

ваш пользовательский контроллер представления контейнера должен будет настроить contentInset второго контроллера вида в соответствии с вашей известной высотой навигационной панели, уважая automaticallyAdjustsScrollViewInsets свойство контроллера представления ребенка. (Вы также можете быть заинтересованы в topLayoutGuide свойство вашего контейнера - убедитесь, что он возвращает правильное значение во время и после переключения вида.)

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

это, кажется, все гораздо проще, чем то, что люди делают.

UINavigationController будет устанавливать вставки scrollview только при размещении подвидов. addChildViewController: не вызовет макет, так что после вызова его, вам просто нужно позвонить setNeedsLayout на navigationController. Вот что я делаю при переключении представлений в пользовательском представлении типа вкладки:

[self addChildViewController:newcontroller];
[self.view insertSubview:newview atIndex:0];
[self.navigationController.view setNeedsLayout];

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

FYI в случае, если у кого-то есть аналогичная проблема: эта проблема может возникнуть даже без встроенных контроллеров вида. Оказывается, что automaticallyAdjustsScrollViewInsets применяется только в том случае, если ваш scrollview (или tableview/collectionview/webview) является первым представлением в иерархии контроллера вида.

Я часто добавляю UIImageView сначала в моей иерархии, чтобы иметь фоновое изображение. Если вы сделаете это, вы должны вручную установить край вставки scrollview в viewDidLayoutSubviews:

- (void) viewDidLayoutSubviews {
    CGFloat top = self.topLayoutGuide.length;
    CGFloat bottom = self.bottomLayoutGuide.length;
    UIEdgeInsets newInsets = UIEdgeInsetsMake(top, 0, bottom, 0);
    self.collectionView.contentInset = newInsets;

}

Я нашел лучшее решение, используйте недокументированный метод UINavigationController.

#import <UIKit/UIKit.h>

@interface UINavigationController (ContentInset)


- (void) computeAndApplyScrollContentInsetDeltaForViewController:(UIViewController*) controller;

@end

#import "UINavigationController+ContentInset.h"

@interface UINavigationController()


- (void)_computeAndApplyScrollContentInsetDeltaForViewController:(id)arg1; 

@end


@implementation UINavigationController (ContentInset)

- (void) computeAndApplyScrollContentInsetDeltaForViewController:(UIViewController*) controller
{
    if ([UINavigationController instancesRespondToSelector:@selector(_computeAndApplyScrollContentInsetDeltaForViewController:)])
        [self _computeAndApplyScrollContentInsetDeltaForViewController:controller];
}

@end

тогда сделай вот так

- (void) cycleFromViewController: (UIViewController*) oldC
                toViewController: (UIViewController*) newC
{
    [oldC willMoveToParentViewController:nil];                        
    [self addChildViewController:newC];
  
    [self transitionFromViewController: oldC toViewController: newC   
                              duration: 0.25 options:0
                            animations:^{
                                newC.view.frame = oldC.view.frame;                                    
                                [self.navigationController computeAndApplyScrollContentInsetDeltaForViewController:newC];
                            }
                            completion:^(BOOL finished) {
                                [oldC removeFromParentViewController];                  
                                [newC didMoveToParentViewController:self];
                            }];
}

задание edgesForExtendedLayout = [] на моем childcontrollers работал для меня.

Comments

    Ничего не найдено.