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

IOS 7: Пользовательский контроллер представления контейнера и вставка содержимого

У меня есть контроллер табличного представления, завернутый в контроллер навигации. Кажется, что контроллер навигации автоматически применяет правильную вставку содержимого к контроллеру табличного представления, когда он представлен через presentViewController:animated:completion:. (Может ли кто-нибудь объяснить мне, как это работает?)

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

Я бы хотел, чтобы не корректировать вставку содержимого вручную или с помощью автоматического макета, поскольку я хочу продолжать поддерживать iOS 5.

4b9b3361

Ответ 1

Благодаря подсказке Johannes Fahrenkrug, я понял следующее: automaticallyAdjustsScrollViewInsets действительно работает только так, как рекламируется, когда корневой вид контроллера просмотра child является UIScrollView. Для меня это означает, что работает следующая компоновка:

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

Пока это не так:

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

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

Ответ 2

У меня была аналогичная проблема. Итак, на iOS 7 в UIViewController есть новое свойство, называемое automaticallyAdjustsScrollViewInsets. По умолчанию установлено значение YES. Когда ваша иерархия представлений более сложна, чем просмотр прокрутки/таблицы внутри контроллера навигации или панели вкладок, это свойство, похоже, не работает для меня.

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

Заголовок:

#import <UIKit/UIKit.h>

@interface SPWKBaseCollectionViewController : UICollectionViewController

@end

Реализация:

#import "SPWKBaseCollectionViewController.h"

@implementation SPWKBaseCollectionViewController


- (void)viewDidLoad
{
    [super viewDidLoad];

    [self updateContentInsetsForInterfaceOrientation:self.interfaceOrientation];
}

- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
    [self updateContentInsetsForInterfaceOrientation:toInterfaceOrientation];

    [super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
}

- (void)updateContentInsetsForInterfaceOrientation:(UIInterfaceOrientation)orientation
{
    if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7) {
        UIEdgeInsets insets;

        if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) {
            insets = UIEdgeInsetsMake(64, 0, 56, 0);
        } else if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
            if (UIInterfaceOrientationIsPortrait(orientation)) {
                insets = UIEdgeInsetsMake(64, 0, 49, 0);
            } else {
                insets = UIEdgeInsetsMake(52, 0, 49, 0);
            }
        }

        self.collectionView.contentInset = insets;
        self.collectionView.scrollIndicatorInsets = insets;
    }
}

@end

Это также работает для веб-просмотров:

self.webView.scrollView.contentInset = insets;
self.webView.scrollView.scrollIndicatorInsets = insets;

Если есть более элегантный и надежный способ сделать это, пожалуйста, дайте мне знать! Значения жестко закодированных вложений плохо пахнут, но я не вижу другого способа, когда вы хотите сохранить совместимость с iOS 5.

Ответ 3

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;

}

Ответ 4

UINavigationController вызывает недокументированный метод _updateScrollViewFromViewController: toViewController при переходе между контроллерами для обновления вложений содержимого. Вот мое решение:

UINavigationController + ContentInset.h

#import <UIKit/UIKit.h>
@interface UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

UINavigationController + ContentInset.m

#import "UINavigationController+ContentInset.h"
@interface UINavigationController()
- (void) _updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to;
@end

@implementation UINavigationController (ContentInset)
- (void) updateScrollViewFromViewController:(UIViewController*) from toViewController:(UIViewController*) to {
    if ([UINavigationController instancesRespondToSelector:@selector(_updateScrollViewFromViewController:toViewController:)])
        [self _updateScrollViewFromViewController:from toViewController:to];
}
@end

Вызов updateScrollViewFromViewController: toViewController где-то в вашем настраиваемом контроллере контейнеров

[self addChildViewController:newContentViewController];
[self transitionFromViewController:previewsContentViewController
                  toViewController:newContentViewController
                          duration:0.5f
                           options:0
                        animations:^{
                            [self.navigationController updateScrollViewFromViewController:self toViewController:newContentViewController];

                    }
                    completion:^(BOOL finished) {
                        [newContentViewController didMoveToParentViewController:self];
                    }];

Ответ 5

Вам не нужно вызывать любые недокументированные методы. Все, что вам нужно сделать, это позвонить setNeedsLayout на ваш UINavigationController. См. Другой ответ: fooobar.com/questions/117322/...

Ответ 6

Быстрое решение

Это решение предназначено для iOS 8 и выше в Swift.

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

Когда пользователь нажимает кнопку навигационной панели, родительский контроллер представления переключается между двумя контроллерами дочерних представлений на своем представлении с использованием API-интерфейса (переключение между отображенными результатами и перечисленными результатами). Контроллер родительского представления содержит ссылки на два дочерних контроллера. Второй контроллер детского представления не создается до тех пор, пока пользователь не закроет кнопку переключения. Второй контроллер детского представления - это контроллер табличного представления, и он обнаружил проблему, когда он перекрывает панель навигации независимо от того, как было установлено свойство automaticallyAdjustsScrollViewInsets.

Чтобы исправить это, я вызываю adjustChild:tableView:insetTop (показано ниже) после того, как создан контроллер представления дочерних таблиц и в методе контроллера родительского представления viewDidLayoutSubviews.

Вид таблицы контроллера детского представления и контроллер родительского представления topLayoutGuide.length передаются в метод adjustChild:tableView:insetTop, подобный этому...

// called right after childViewController is created
adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)

Метод adjustChild...

private func adjustChild(tableView: UITableView, insetTop: CGFloat) {
    tableView.contentInset.top = insetTop
    tableView.scrollIndicatorInsets.top = insetTop

    // make sure the tableview is scrolled at least as far as insetTop
    if (tableView.contentOffset.y > -insetTop) {
        tableView.contentOffset.y = -insetTop
    }
}

Оператор tableView.scrollIndicatorInsets.top = insetTop настраивает индикатор прокрутки в правой части таблицы, поэтому он начинается чуть ниже навигационной панели. Это тонко и легко упускается из виду, пока вы не осознаете это.

Ниже показано, как выглядит viewDidLayoutSubviews...

override func viewDidLayoutSubviews() {
    if let childViewController = childViewController {
        adjustChild(childViewController.tableView, insetTop: topLayoutGuide.length)
    }
}

Обратите внимание, что весь приведенный выше код появляется в родительском контроллере представления.

Я понял это с помощью Кристофер Пикслей ответить на вопрос "iOS 7 Table view не удается настроить настройку содержимого".