iOS 7: UITableView отображается под строкой состояния
Первый экран моего приложения UITableViewController
без панели навигации, что означает, что контент течет под строкой состояния, поэтому возникает много текстовых коллизий. Я настроил оба свойства для Under top bars
а также Adjust scroll view insets
которые на самом деле мешают прокрутке внизу, но ценой сохранения верхней части таблицы под. Я пытался установить UITableView
смещение кадра на 20 пикселей, но оно, кажется, не вступает в силу, и, поскольку мне нужно, чтобы приложение было совместимо с iOS 6, я не могу перейти к раскадровке iOS 7, чтобы принудительно использовать автоматическую компоновку для использования верхнего указателя высоты. Кто-нибудь нашел решение, которое работает для обеих версий?
Вещи, которые я пробовал: установка edgesForExtendedLayout
, изменяя настройки в раскадровке для Under top bars
а также Adjust scroll view
, заставляя кадр в новую область.
Одна картинка стоит тысячи слов:
26 ответов
Для тех, кто заинтересован в тиражировании, просто выполните следующие действия:
- Создать новый проект iOS
- Откройте основную раскадровку и удалите по умолчанию / начальный
UIViewController
- Вытащить новый
UITableViewController
из библиотеки объектов - Установите его в качестве начального контроллера просмотра
- Накорми таблицу некоторыми тестовыми данными
Если вы выполните вышеуказанные шаги, при запуске приложения вы увидите, что ничто, в том числе настройка флажков Xcode на "Расширить края под полосами {Top, Bottom, Opaque}", не будет препятствовать появлению первой строки под строкой состояния, и вы не можете решить эту проблему программно.
Например, в приведенном выше сценарии следующее не будет иметь эффекта:
// These do not work
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
Эта проблема может быть очень неприятной, и я считаю, что это ошибка на стороне Apple, особенно потому, что она обнаруживается в их собственных предварительно смонтированных UITableViewController
из библиотеки объектов.
Я не согласен со всеми, кто пытается решить эту проблему, используя любую форму "Магических чисел", например "использовать дельту в 20 пикселей". Этот вид тесно связанных программ определенно не то, что Apple хочет, чтобы мы делали здесь.
Я обнаружил два решения этой проблемы:
Сохранение
UITableViewController
Сцена:
Если вы хотите сохранитьUITableViewController
в раскадровке, не помещая ее вручную в другое представление, вы можете встроитьUITableViewController
вUINavigationController
(Редактор> Встроить> Контроллер навигации) и снимите флажок "Показывает панель навигации" в инспекторе. Это решает проблему без дополнительной настройки, а также сохраняет вашиUITableViewController
Сцена в раскадровке.Использование AutoLayout и встраивание
UITableView
в другую точку зрения (я считаю, что именно так Apple хочет, чтобы мы это делали):
Создать пустойUIViewController
и перетащитеUITableView
в этом. Затем Ctrl-перетащите с вашегоUITableView
в сторону строки состояния. Когда мышь достигнет нижней части строки состояния, вы увидите всплывающую подсказку с надписью "Top Layout Guide". Отпустите кнопку мыши и выберите "Вертикальный интервал". Это скажет системе макета разместить ее прямо под строкой состояния.
Я протестировал оба способа на пустом приложении, и они оба работают. Возможно, вам придется сделать некоторые дополнительные настройки, чтобы они работали для вашего проекта.
Если вы делаете вещи программно и используете UITableViewController
без UINavigationController
Лучше всего сделать следующее в viewDidLoad
:
Свифт 3
self.tableView.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 0, right: 0)
Ранее Свифт
self.tableView.contentInset = UIEdgeInsetsMake(20.0f, 0.0f, 0.0f, 0.0f);
UITableViewController
будет прокручиваться за строкой состояния, но не будет находиться под ней при прокрутке вверх.
Обратите внимание: это сработало для меня для следующей конфигурации:
- Нет панели навигации в верхней части экрана (представление таблицы соответствует строке состояния)
- Табличное представление не прокручивается
Если два вышеуказанных требования не выполнены, ваш пробег может отличаться.
Оригинальный пост
Я создал свой вид программно, и это сработало для меня:
- (void) viewDidLayoutSubviews {
// only works for iOS 7+
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
CGRect viewBounds = self.view.bounds;
CGFloat topBarOffset = self.topLayoutGuide.length;
// snaps the view under the status bar (iOS 6 style)
viewBounds.origin.y = topBarOffset * -1;
// shrink the bounds of your view to compensate for the offset
viewBounds.size.height = viewBounds.size.height + (topBarOffset * -1);
self.view.bounds = viewBounds;
}
}
Источник (в разделе topLayoutGuide внизу стр.39).
Добавление к верхнему ответу:
после того, как 2-й метод изначально не работал, я немного поработал и нашел решение.
TLDR; 2-е решение верхнего ответа почти работает, но для некоторых версий xCode ctrl+ перетаскивание в "Top Layout Guide" и выбор вертикального интервала ничего не делает. Тем не менее, сначала отрегулировав размер табличного представления, а затем выбрав "Руководство по макету сверху вниз", можно работать.
Перетащите пустой ViewController на раскадровку.
Перетащите объект UITableView в представление. (Не UITableViewController). Расположите его в самом центре, используя синие направляющие.
- Перетащите UITableViewCell в TableView. Это будет ваша ячейка повторного использования прототипа, поэтому не забудьте установить ее Идентификатор повторного использования на вкладке Атрибуты, иначе вы получите сбой.
Создайте свой собственный подкласс UIViewController и добавьте
<UITableViewDataSource, UITableViewDelegate>
протоколы. Не забудьте установить ViewController вашей раскадровки на этот класс в Identity Inspector.Создайте выход для вашего TableView в файле реализации и назовите его "tableView"
- Щелкните правой кнопкой мыши TableView и перетащите как источник данных, так и делегата в свой ViewController.
Теперь для части не вырезать в строке состояния.
- Возьмите верхний край вашего табличного представления и переместите его вниз к одной из пунктирных синих направляющих автоматической разметки, которые находятся ближе к верху
- Теперь вы можете управлять перетаскиванием из табличного представления в верхнюю часть и выбирать "Руководство по макету сверху".
- Это даст вам ошибку о неоднозначной компоновке TableView, просто добавьте недостающие ограничения и все готово.
Теперь вы можете настроить свой вид таблицы как обычно, и он не будет обрезать строку состояния!
- (void) viewDidLayoutSubviews {
if (floor(NSFoundationVersionNumber) > NSFoundationVersionNumber_iOS_6_1) {
self.navigationController.navigationBar.barStyle = UIBarStyleBlackOpaque;
if ([self respondsToSelector:@selector(edgesForExtendedLayout)])
self.edgesForExtendedLayout = UIRectEdgeNone; // iOS 7 specific
CGRect viewBounds = self.view.bounds;
CGFloat topBarOffset = self.topLayoutGuide.length;
viewBounds.origin.y = topBarOffset * -1;
self.view.bounds = viewBounds;
self.navigationController.navigationBar.translucent = NO;
}
}
Вот как написать это в "Swift" - корректировку ответа @lipka:
tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)
Выберите UIViewController на вашей раскадровке и снимите флажок Расширить края под верхними полосами. Работал на меня.:)
Это исправит это для UITableViewController (без каких-либо магических чисел). Единственное, что мне не удалось исправить, это то, что вы разговариваете по телефону, и в этом случае верхняя часть tableView слишком сильно надавливается. Если кто-нибудь знает, как решить эту проблему, пожалуйста, сообщите нам.
class MyTableViewController: UITableViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureTableViewTop()
}
override func viewWillTransitionToSize(size: CGSize, withTransitionCoordinator coordinator: UIViewControllerTransitionCoordinator) {
super.viewWillTransitionToSize(size, withTransitionCoordinator: coordinator)
coordinator.animateAlongsideTransition({ (context) -> Void in
}, completion: { (context) -> Void in
self.configureTableViewTop()
})
}
func configureTableViewTop() {
tableView.contentInset.top = UIApplication.sharedApplication().statusBarFrame.height
}
}
Работает на swift 3 - во viewDidLoad();
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let insets = UIEdgeInsets(top: statusBarHeight, left: 0, bottom: 0, right: 0)
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets
Этот код: 1. Получает высоту строки состояния. 2. Предоставляет в верхней части таблицы представление содержимого, равное высоте строки состояния. 3. Придает индикатору прокрутки такую же вставку, чтобы он отображался под строкой состояния.
Ответ chappjc прекрасно работает при работе с XIB.
Я обнаружил, что самое чистое решение при создании TableViewControllers программным способом - это обернуть экземпляр UITableViewController в другой UIViewController и установить соответствующие ограничения.
Вот:
UIViewController *containerLeftViewController = [[UIViewController alloc] init];
UITableViewController *tableViewController = [[UITableViewController alloc] init];
containerLeftViewController.view.backgroundColor = [UIColor redColor];
hostsAndMoreTableViewController.view.translatesAutoresizingMaskIntoConstraints = NO;
[containerLeftViewController.view addSubview:tableViewController.view];
[containerLeftViewController addChildViewController:tableViewController];
[tableViewController didMoveToParentViewController:containerLeftViewController];
NSDictionary * viewsDict = @{ @"tableView": tableViewController.view ,
@"topGuide": containerLeftViewController.topLayoutGuide,
@"bottomGuide": containerLeftViewController.bottomLayoutGuide,
};
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[tableView]|"
options:0
metrics:nil
views:viewsDict]];
[containerLeftViewController.view addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"V:[topGuide][tableView][bottomGuide]"
options:0
metrics:nil
views:viewsDict]];
Ура, Бен
Я не знаю, насколько он кошерный, но я обнаружил, что эта схема сдвигает представление ViewController вниз и обеспечивает строку состояния сплошным фоном:
- (void)viewDidLoad {
[super viewDidLoad];
// Shove everything down if iOS 7 or later
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
// Move the view down 20 pixels
CGRect bounds = self.view.bounds;
bounds.origin.y -= 20.0;
[self.view setBounds:bounds];
// Create a solid color background for the status bar
CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
statusBar.backgroundColor = [UIColor redColor];
[self.view addSubview:statusBar];
}
Конечно, заменить redColor
с любым цветом, который вы хотите для фона.
Вы должны отдельно сделать один из вариантов, чтобы установить цвет символов / символов в строке состояния. я использую View controller-based status bar appearance = NO
а также Status bar style = Opaque black style
в plist, чтобы сделать это глобально.
Кажется, работает, и мне было бы интересно услышать о каких-либо ошибок или проблем с ним.
Я думаю, что подход к использованию UITableViewController может немного отличаться от того, что вы делали раньше. Это сработало для меня, но вы, возможно, не фанат этого. Я сделал контроллер представления с контейнером, который указывает на мой UItableViewController. Таким образом, я могу использовать TopLayoutGuide, предоставленный в раскадровке. Просто добавьте ограничение к представлению контейнера, и вы должны позаботиться об iOS7 и iOS6.
Я сделал это для отображения Retina/Non-Retina как
BOOL isRetina = FALSE;
if ([[UIScreen mainScreen] respondsToSelector:@selector(scale)]) {
if ([[UIScreen mainScreen] scale] == 2.0) {
isRetina = TRUE;
} else {
isRetina = FALSE;
}
}
if (isRetina) {
self.edgesForExtendedLayout=UIRectEdgeNone;
self.extendedLayoutIncludesOpaqueBars=NO;
self.automaticallyAdjustsScrollViewInsets=NO;
}
Следующее решение работает достаточно хорошо в коде без использования магических констант и учитывает изменение пользователем класса размера, например, с помощью ротации или параллельных приложений на ipads:
- (void)traitCollectionDidChange:(UITraitCollection *)previousTraitCollection {
[super traitCollectionDidChange:previousTraitCollection];
// Fix up content offset to ensure the tableview isn't underlapping the status bar.
self.tableView.contentInset = UIEdgeInsetsMake(self.topLayoutGuide.length, 0.0, 0.0, 0.0);
}
Я закончил тем, что использовал одно дополнительное представление с желаемым фоном, добавленное после TableView и помещенное под строку состояния:
self.CoverView = [[UIView alloc]init];
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 7.0) {
self.CoverView.frame = CGRectMake(0,0,self.view.bounds.size.width,20);
}
self.CoverView.backgroundColor = [UIColor whiteColor];
self.TableView = [[UITableView alloc]initWithFrame:CGRectMake(0,
self.CoverView.bounds.size.height,XXX, YYY)];
[self.view addSubview:self.TableView];
[self.view addSubview:self.CoverView];
Это не очень красиво, но это довольно простое решение, если вам нужно работать с представлениями без Xib, а также с IOS6 и IOS7
Для таких, как я, которые предпочли бы не вставлять свои UITableViewController
в UIViewController
попробуй это:
Обычай UITableViewController
Подкласс может добавить представление маски к суперпредставлению tableView. Добавьте маску в viewDidAppear и удалите маску в viewWillDisappear.
private var statusBarMaskView: UIView!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if let superview = self.tableView.superview {
self.statusBarMaskView = UIView(frame: CGRect.zero)
superview.insertSubview(self.statusBarMaskView, aboveSubview: self.tableView)
self.statusBarMaskView.backgroundColor = self.tableView.backgroundColor
// using a nice constraint layout library to set the frame
self.statusBarMaskView.pinTop(to: superview)
self.statusBarMaskView.pinLeft(to: superview)
self.statusBarMaskView.pinRight(to: superview)
self.statusBarMaskView.addHeightConstraint(with: 22.0)
////
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.statusBarMaskView.removeFromSuperview()
self.statusBarMaskView = nil
}
Я нашел, что самый простой способ сделать это, особенно если вы добавляете представление таблицы внутри панели вкладок, - это сначала добавить представление, а затем добавить представление таблицы внутри этого представления. Это дает вам направляющие верхнего поля, которые вы ищете.
Решение Абрахамчеза https://developer.apple.com/library/ios/qa/qa1797/_index.html работало для меня следующим образом. У меня был один UITableviewcontroller в качестве моего начального представления. Я пробовал код смещения и встраивание в navcon, но ни один не решил прозрачность строки состояния.
Добавьте Viewcontroller и сделайте его начальным представлением. Это должно показать вам важные руководства по макетам верха и низа.
Перетащите старый табличный вид в представление в новом контроллере.
Сделайте все, чтобы переставить стол в новый контроллер:
Измените старый файл view controller.h, чтобы он наследовал / подкласс от UIViewController вместо UITableViewController.
Добавьте UITableViewDataSource и UITableViewDelegate в.h контроллера вида.
Повторно подключите все, что нужно в раскадровке, например панель поиска.
Главное - установить ограничения, как в вопросах и ответах Apple. Я не стал вставлять панель инструментов. Не уверен в точной последовательности. Но красный значок появился на руководствах по макету, возможно, когда я построил. Я щелкнул по нему и позволил Xcode установить / очистить ограничения.
Затем я нажимал везде, пока не нашел ограничение вертикального пространства и изменил его верхнее значение с -20 на 0, и оно заработало отлично.
override func viewDidLoad() {
// your code
if let nc = self.navigationController {
yourView.frame.origin.y = nc.navigationBar.frame.origin.y + nc.navigationBar.frame.height
}
}
У меня был UISearchBar в верхней части моего UITableView, и следующее работало;
self.tableView.contentInset = UIEdgeInsetsMake(20, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -20);
Поделитесь и наслаждайтесь...
Вот Свифт 2.3. (Xcode 8.0) решение. Я создал подкласс UITableView.
class MYCustomTableView: UITableView
{
override func drawRect(rect: CGRect) {
super.drawRect(rect)
contentInset = UIEdgeInsetsZero
}
}
Вкладка содержимого всегда должна быть нулевой (по умолчанию). Я устанавливаю ноль вручную. Вы также можете добавить метод проверки, который выполняет проверку, и если это что-то отличное от того, что вы хотите, просто получите правильный прямоугольник. Изменение отразится только тогда, когда будет отображено представление таблицы (что случается не часто).
Не забудьте обновить TableView в вашем IB (в TableViewController или просто в TableView внутри вашего ViewController).
Я столкнулся с этой проблемой в ios 11, но макет был правильным для ios 8 - 10.3.3 . Для моего случая я установил ограничение вертикального пространства в Superview.Top Margin вместо Superview.Top, которое работает для ios 8 - 11.
Я использую UISplitViewController с контроллером навигации и контроллером таблицы. Это сработало для меня в главном представлении после того, как я попробовал много решений здесь:
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
// Move the view down 20 pixels
CGRect bounds = self.view.bounds;
bounds.origin.y -= 20.0;
[self.navigationController.view setBounds:bounds];
// Create a solid color background for the status bar
CGRect statusFrame = CGRectMake(0.0, -20.0, bounds.size.width, 20);
UIView* statusBar = [[UIView alloc] initWithFrame:statusFrame];
statusBar.backgroundColor = [UIColor whiteColor];
[statusBar setAlpha:1.0f];
[statusBar setOpaque:YES];
[self.navigationController.view addSubview:statusBar];
}
Это похоже на решение Hot Licks, но применяет подпредставление к navigationController.
Увидеть все решения: мой проект просто использует xib, поэтому решение с раскадровкой не работает для меня. self.edgesForExtendedLayout = UIRectEdgeNone; просто работает для контроллера, если панель навигации видна. но если у вашего представления просто есть строка состояния, это не сработает. поэтому я объединяю два условия.
- (void) viewDidLayoutSubviews {
float systemVersion = [[[UIDevice currentDevice] systemVersion] floatValue];
if (systemVersion >= 7.0f) {
CGRect bounds = self.view.bounds;
if(self.navigationController == nil || self.navigationController.isNavigationBarHidden == YES){
bounds.origin.y -= 20.0;
[self.view setBounds:bounds];
}
else{
self.edgesForExtendedLayout = UIRectEdgeNone;
}
}
помочь это работает.
Если вам также нужна поддержка iOS 6, вам придется условно переместить ее вниз. То есть в iOS 7 вы должны просто сдвинуть его вниз на 20 пунктов (либо через frame
манипуляции или использование авто-макета), и в iOS 6 вы оставляете это в покое. Я не верю, что вы можете сделать это в IB, поэтому вам придется делать это в коде.
РЕДАКТИРОВАТЬ
На самом деле вы можете сделать это в IB, используя дельты iOS6/iOS7. Установите свою позицию в iOS 7, затем для iOS 6 установите дельта Y на -20 баллов. Смотрите этот вопрос так для получения дополнительной информации.