IOS 7: Пользовательский контроллер представления контейнера и вставка содержимого
У меня есть контроллер табличного представления, завернутый в контроллер навигации. Кажется, что контроллер навигации автоматически применяет правильную вставку содержимого к контроллеру табличного представления, когда он представлен через presentViewController:animated:completion:
. (Может ли кто-нибудь объяснить мне, как это работает?)
Однако, как только я завершу комбинацию в пользовательском контроллере представления контейнера и представляю, что вместо этого верхняя часть содержимого представления таблицы скрыта за панелью навигации. Есть ли что-то, что я могу сделать, чтобы сохранить поведение автоматической вставки содержимого в этой конфигурации? Должен ли я "пропустить" что-то в контроллере контейнера, чтобы это работало правильно?
Я бы хотел, чтобы не корректировать вставку содержимого вручную или с помощью автоматического макета, поскольку я хочу продолжать поддерживать iOS 5.
Ответы
Ответ 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 не удается настроить настройку содержимого".