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 ответов

Решение

Для тех, кто заинтересован в тиражировании, просто выполните следующие действия:

  1. Создать новый проект iOS
  2. Откройте основную раскадровку и удалите по умолчанию / начальный UIViewController
  3. Вытащить новый UITableViewController из библиотеки объектов
  4. Установите его в качестве начального контроллера просмотра
  5. Накорми таблицу некоторыми тестовыми данными

Если вы выполните вышеуказанные шаги, при запуске приложения вы увидите, что ничто, в том числе настройка флажков 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" и выбор вертикального интервала ничего не делает. Тем не менее, сначала отрегулировав размер табличного представления, а затем выбрав "Руководство по макету сверху вниз", можно работать.


  1. Перетащите пустой ViewController на раскадровку. Контроллер представления

  2. Перетащите объект UITableView в представление. (Не UITableViewController). Расположите его в самом центре, используя синие направляющие.

Табличное представлениеЦентр изображения

  1. Перетащите UITableViewCell в TableView. Это будет ваша ячейка повторного использования прототипа, поэтому не забудьте установить ее Идентификатор повторного использования на вкладке Атрибуты, иначе вы получите сбой.

Добавить ячейку табличного представленияИдентификатор повторного использования

  1. Создайте свой собственный подкласс UIViewController и добавьте <UITableViewDataSource, UITableViewDelegate> протоколы. Не забудьте установить ViewController вашей раскадровки на этот класс в Identity Inspector.

  2. Создайте выход для вашего TableView в файле реализации и назовите его "tableView"

Создать аутлетTableview

  1. Щелкните правой кнопкой мыши TableView и перетащите как источник данных, так и делегата в свой ViewController.

делегат источника данных

Теперь для части не вырезать в строке состояния.

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

изменить размер

  1. Теперь вы можете управлять перетаскиванием из табличного представления в верхнюю часть и выбирать "Руководство по макету сверху".

Руководство по макету сверху вниз

  1. Это даст вам ошибку о неоднозначной компоновке 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;
    }
}

https://developer.apple.com/library/ios/documentation/userexperience/conceptual/TransitionGuide/SupportingEarlieriOS.html

Вот как написать это в "Swift" - корректировку ответа @lipka:

tableView.contentInset = UIEdgeInsetsMake(20.0, 0.0, 0.0, 0.0)

Выберите UIViewController на вашей раскадровке и снимите флажок Расширить края под верхними полосами. Работал на меня.:)

Для Xcode 7 снятие галочки с "полупрозрачной" галочки на панели навигации сработало для меня.

введите описание изображения здесь

Это исправит это для 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 баллов. Смотрите этот вопрос так для получения дополнительной информации.

Другие вопросы по тегам