Вот моя оболочка для решения от iOS 9 до iOS 11+ в Swift 4+

стимо ли руководство по безопасной разметке с iOS ниже 11?

 Martin R12 сент. 2017 г., 21:55
Есть ли особая причина пометить вопрос с помощью [iphone-x]?
 Martin R12 сент. 2017 г., 21:52
Первый хит Google для "руководства по макету безопасной зоны"useyourloaf.com/blog/safe-area-layout-guideгде утверждается, что«... вы можете переключиться на использование руководства по макету безопасной области в Интерфейсном Разработчике, даже если вы по-прежнему ориентированы на iOS 10 и старше».
 Yuji13 сент. 2017 г., 00:32
Я думаю, что я знаю, почему на iPhoneX, TabBar будет облажаться без SafeareaInsets
 rmaddy13 сент. 2017 г., 16:32
Если для вашей цели развертывания установлено что-либо ниже iOS 9.0, Xcode выдаст вам ошибку, что руководства по макету безопасной области не поддерживаются.
 Pushparaj05 окт. 2017 г., 13:29
Есть ли какая-либо возможность использовать поддержку безопасной зоны и поддержку iOS 8? я думаю, чтобы добавить программно на все экраны ..

Ответы на вопрос(13)

который расширяют все ваши ViewController, другим решением будет поместить элементы, которые должны быть отрегулированы, в IBOutletCollection и программно настроить их в этом GenericViewController. Вот мой код:

@IBOutlet var adjustTopSpaceViews: [UIView]?

override func viewDidLoad() {
    super.viewDidLoad()
    adjustViews()
    ....
}

func adjustViews() {
    guard let views = adjustTopSpaceViews,
        ProcessInfo.processInfo.operatingSystemVersion.majorVersion < 11 else {
            return
    }
    let statusBarHeight = UIApplication.shared.statusBarFrame.height
    for subview in views {
        subview.superview?.constraints.filter({ (constraint) -> Bool in
            return constraint.firstAttribute == .top
                && constraint.secondAttribute == .top
                && (constraint.firstItem as? UIView == subview || constraint.secondItem as? UIView == subview)
        }).forEach({ (constraint) in
            constraint.constant += (constraint.firstItem as? UIView == subview) ? statusBarHeight : -statusBarHeight
        })
    }
}

когда на iPhone-X

if (@available(iOS 11, *)) {

    NSLayoutConstraint *bottomConstraint   = [NSLayoutConstraint constraintWithItem:self.childView
                                                                              attribute:NSLayoutAttributeBottom
                                                                              relatedBy:NSLayoutRelationEqual
                                                                                 toItem:self.parentView.safeAreaLayoutGuide
                                                                              attribute:NSLayoutAttributeBottom
                                                                             multiplier:1.0
                                                                               constant:0];


    NSLayoutConstraint *topConstraint   = [NSLayoutConstraint constraintWithItem:self.childView
                                                                       attribute:NSLayoutAttributeTop
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:self.parentView.safeAreaLayoutGuide
                                                                       attribute:NSLayoutAttributeTop
                                                                      multiplier:1.0
                                                                        constant:0];


} else {

    NSLayoutConstraint *bottomConstraint   = [NSLayoutConstraint constraintWithItem:self.childView
                                                                          attribute:NSLayoutAttributeBottom
                                                                          relatedBy:NSLayoutRelationEqual
                                                                             toItem:self.parentView
                                                                          attribute:NSLayoutAttributeBottom
                                                                         multiplier:1.0
                                                                           constant:0];


    NSLayoutConstraint *topConstraint   = [NSLayoutConstraint constraintWithItem:self.childView
                                                                       attribute:NSLayoutAttributeTop
                                                                       relatedBy:NSLayoutRelationEqual
                                                                          toItem:self.parentView
                                                                       attribute:NSLayoutAttributeTop
                                                                      multiplier:1.0
                                                                        constant:0];

}

по крайней мере, одна проблема обратной совместимости с ограничениями безопасной области iOS 11, которую я наблюдал в Xcode 9 GM - контроллеры push-представления с ограничениями безопасной области.

Если ваша навигационная панель скрыта, и вы выдвигаете представление верхней области безопасной области, то толкнутое представление будет перекрывать строку состояния в iOS 9 и 10.

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

На iOS 11 раскладка будет правильной в обоих случаях.

Вот простой пример:http://www.filedropper.com/foobar

И вот видео этого со скрытой панелью навигации (iOS 10.3 слева, iOS 11 справа):https://vimeo.com/234174841/1e27a96a87

Вот версия, где видна навигационная панель (включена в перо):https://vimeo.com/234316256/f022132d57

Я подал это как Радар, # 34477706.

Благодаря@Шлифовальная машинка для указания на видимый случай навигационной панели.

 clarus18 сент. 2017 г., 15:04
вау, ты прав. Я пропустил это.
 Sander18 сент. 2017 г., 13:53
Если панель навигации не скрыта, ошибка остается прежней. На версиях iOS 9 и 10 нажатой вид выложен под панелью навигации. Или этот случай должен быть обработан по-другому?
 clarus18 сент. 2017 г., 15:27
Итак, с точки зрения обходных путей, на ум приходит несколько вещей, ни одна из которых не является хорошей, но ... 1) Вы можете иметь отдельные перья / раскадровки для пред-iOS 11. В зависимости от сложности вашего приложения, это может быть настоящая боль или супер просто. или 2) Если панель навигации скрыта, имеет верхнее ограничение> 20 для суперпредставления для представлений, которые имеют строку состояния, но не имеют навигацию (с приоритетом 1000), а также безопасную область (с приоритетом 750). Это работает и не должно вызывать проблем в будущем. Для видимого случая navbar, это более сложно. Я все еще работаю над хорошим решением для этого.
 Annjawn11 нояб. 2017 г., 17:33
Да, это. Это нелепо. Мне пришлось использовать множество обходных путей, чтобы заставить вещи хорошо играть в iOS 9 и 10. Это способ Apple сказать: «Да, извините, что слышал об этом ..... но нас не волнует ios10 или 9». :)
 ernewston28 сент. 2017 г., 16:56
Привет @clarus, спасибо за информацию! Получили ли вы какой-либо ответ от Apple?

«Руководство по разметке безопасной зоны» обратно совместимо.. С раскадровкой вроде нормально.

Я решил свою проблему, открыв «Ограничение верхнего макета» из первого объекта в верхней части моего представления.

@property (weak, nonatomic) IBOutlet NSLayoutConstraint *topLayoutConstraint;

затем я изменил значение константы на это ограничение и обновил представление. Например, если вы используете панель навигации (высота 44) и строку состояния (высота 20):

if (SYSTEM_VERSION_LESS_THAN(@"11.0")) {
    _topLayoutConstraint.constant = 64;
    [self.view layoutIfNeeded];
}

С SYSTEM_VERSION_LESS_THAN, который определен так:

#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending)

NSLayoutConstraintэто прикреплено к вашемуsafeArea.

Это немного странно, поскольку вы должны получить ViewController из UIView, но, на мой взгляд, это простая и хорошая альтернатива, пока Apple окончательно не исправит обратную совместимость для safeArea в Xibs.

Подкласс:

class SafeAreaBackwardsCompatabilityConstraint: NSLayoutConstraint {
private weak var newConstraint: NSLayoutConstraint?

override var secondItem: AnyObject? {
    get {
        if #available(iOS 11.0, *) {}
        else {
            if let vc = (super.secondItem as? UIView)?.parentViewController, newConstraint == nil {
                newConstraint = (self.firstItem as? UIView)?.topAnchor.constraint(equalTo: vc.topLayoutGuide.bottomAnchor)
                newConstraint?.isActive = true
                newConstraint?.constant = self.constant
            }
        }
        return super.secondItem
    }
}

override var priority: UILayoutPriority {
    get {
        if #available(iOS 11.0, *) { return super.priority }
        else { return 750 }
    }
    set { super.priority = newValue }
}
}

private extension UIView {
var parentViewController: UIViewController? {
    var parentResponder: UIResponder? = self
    while parentResponder != nil {
        parentResponder = parentResponder!.next
        if let viewController = parentResponder as? UIViewController {
            return viewController
        }
    }
    return nil
}
}

XIb:

ваш проект / приложение будет работать в версиях iOS до iOS 11 без каких-либо проблем. В версиях iOS, предшествующих 11, он заменяет / рассматривает макет безопасной области на обычный AutoLayout и следует правилам макета «Верхняя и нижняя части».

Я протестировал свой существующий проект с SafeAreaLayout и без него на обеих платформах (iOS 11 и более поздняя iOS 10). Работает нормально.

Просто убедитесь:

Если вы разработали свой проект / пользовательский интерфейс в AutoLayout; ограничения вашего UIElement следуют / относятся к руководству по макету сверху и снизу (не для супервидения). Таким образом, одним щелчком (включением) опции SafeAreaLayout автоматически будет правильно реализован макет SafeArea для всех файлов Interface Builders в раскадровке.

Если вы разработали свой проект / пользовательский интерфейс в SafeAreaLayout; тогда он будет автоматически следовать указаниям вершины и низа в отсталой iOS.

Вот пример снимка с результатом. Включение или отключение макета безопасной области не влияет на существующий дизайн.

Схема безопасной зоны:

AutoLayout

Короче говоря, ответ на ваш вопрос: «Включение руководств по макету безопасной области, совместимых с iOS до 11»

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

 Krunal14 сент. 2017 г., 19:31
Если вы работаете с Xcode 9, то ваш макет безопасной области автоматически преобразует безопасную область в макет сверху и снизу для iOS 10 и обратно. Можете ли вы поделиться снимком для макета раскадровки?
 Vyachaslav Gerchicov23 окт. 2017 г., 10:05
бесполезный ответ - это не сработает для iOS 9/10
 Tiago Lira14 сент. 2017 г., 18:31
странно, что руководство по безопасной области у меня не работает в iOS 10 ... оно просто принимает значение 0 и контент перекрывает строку состояния. В iOS 11 работает нормально. Есть идеи почему?
 Tiago Lira14 сент. 2017 г., 19:40
Я обнаружил, что он хорошо преобразует его в раскадровки, но не работает в файлах XIB ... он просто игнорирует размер строки состояния в iOS 10
 Tiago Lira14 сент. 2017 г., 20:02
В моих тестах он отлично работает для iOS 11, не работает на iOS 10. Похоже, в этом материале все еще есть ошибки. Я выложу полный вопрос позже, спасибо за помощь.

ы и поддерживать обратную совместимость с iOS 9 и iOS 10:(РЕДАКТИРОВАТЬ: как указано в комментариях @NickEntin, эта реализация будет предполагать наличие строки состояния, что не будет соответствовать действительности в iPhone X. В результате много места до верха (20 баллов). Это будет работать отлично, однако.

Например. если вы хотите, чтобы изображение было на 10 пунктов ниже строки состояния (и на 10 пунктов ниже корпуса датчика на iPhone X):

В вашем XIB перейдите кFile Inspector и включить сейф, проверивUse Safe Area Layout Guides.Создайте ограничение от вершины представления к вершине основного представления, с>= (больше или равно) ограничение, постоянное30 (30, потому что мы хотим, чтобы интервал в 10 баллов к строке состояния был выше 20 баллов) и приоритетHigh (750).Создайте ограничение от вершины представления до вершины безопасной области, с= (равное) ограничение, постоянное10 и приоритетLow (250).

То же самое можно сделать для вида внизу (и для движения вперед / назад или влево / вправо в безопасную зону):

В вашем XIB перейдите кFile Inspector и включить сейф, проверивUse Safe Area Layout Guides.Создайте ограничение от нижней части представления к основному основанию, с>= (больше или равно) ограничение, постоянное10 и приоритетHigh (750).Создайте ограничение от нижней части представления до нижней части безопасной области, с= (равное) ограничение, постоянное10 и приоритетLow (250).
 NightFury02 янв. 2018 г., 10:55
Безопасный макет является основным видом в xcode 9. Как мы собираемся применить пункт 2? Здесь, во 2 и 3, не будем ли мы создавать ограничения с одинаковым видом?
 thijsonline20 дек. 2017 г., 10:31
@NickEntin спасибо, что заметили. AFAIK строка состояния действительно всегда скрыта в ландшафтном режиме на iPhone X, и эта реализация не справляется с этим (действует так, как будто строка состояния фактически присутствует).
 Nick Entin19 дек. 2017 г., 15:36
Интересно, вы заметили, что на iPhoneX нет строки состояния в ландшафтном режиме, даже если приложение хочет показать это? Если мои наблюдения верны, то ограничение более высокого приоритета, чтобы строка состояния покрытия расстояния перекрывала 0 для безопасной области, и это будет выглядеть не очень хорошо на iPhoneX

Safe Area на iOS 9. По какой-то причине WKWebView просто игнорирует настройки макета безопасной области.

 Nik18 сент. 2017 г., 17:15
Я исправил это, переместив init кадра представления из viewDidLoad в viewWillAppear. Совершенно очевидно, но это не было необходимо для iOS 10, iOS 11. Новые версии iOS выбирают правильный размер после инициализации представления.
 clarus18 сент. 2017 г., 15:33
Вы продвигаете это, представляете это или это корневой вид вашего приложения?

Вот моя оболочка для решения от iOS 9 до iOS 11+ в Swift 4+

        if #available(iOS 11.0, *) {
        safeAreaTopAnchor = contentView.safeAreaLayoutGuide.topAnchor
    } else {
        // Fallback on earlier versions

        var parentViewController: UIViewController? {
            var parentVCResponder: UIResponder? = self
            while parentVCResponder != nil {
                parentVCResponder = parentVCResponder!.next
                if let viewController = parentVCResponder as? UIViewController {
                    return viewController
                }
            }
            return nil
        }

        safeAreaTopAnchor = parentViewController?.topLayoutGuide.bottomAnchor

    }

вы используете SafeArea в xib, вы можете добавить вviewDidLoad:

if (@available(iOS 11.0, *)) {}
else {
    self.edgesForExtendedLayout = UIRectEdgeNone;
}
 Johnny06 дек. 2018 г., 20:58
Да, ты прав. Это исправление положения работает только с панелью навигации. Это не работает без панели навигации.
 LordParsley23 мая 2018 г., 12:24
Сильно помог с этим дополнительным полем в верхней части представления, равным разметке, обычно растянутой под панелью навигации, которая просто находится в видимой области на iOS 9 и 10.

В моем случае оба моиtopConstraint а такжеbottomConstraintс@IBOutlets. Это также совместимо дляiOS 8.

Моя первоначальная конфигурация для верхних и нижних ограничений относится к обычным айфонам, поэтому я редактирую только ограничения для iPhone X

    // iOS 11 Layout Fix. (For iPhone X)
    if #available(iOS 11, *) {
        self.topConstraint.constant = self.topConstraint.constant + self.view.safeAreaInsets.top

        self.bottomConstraint.constant = self.bottomConstraint.constant + self.view.safeAreaInsets.bottom
    }

.

НОТА: self.view ваш superView, поэтому я использую его дляsafeAreaInsets

то на ios 10 у них нет направляющих макетов. Поэтому переместите xib на раскадровку, чтобы обеспечить обратную совместимость.

ри использовании раскадровок. Если вы используете xibs, нет руководства по компоновке, к которому можно обратиться.https://forums.developer.apple.com/thread/87329

Обходные пути, кажется, либо

(а) перенести свои xibs в раскадровки, или

(б) добавить некоторые дополнительные ограничения программно.

Если (а) на самом деле не вариант, ручной подход будет выглядеть примерно так:

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

Добавьте ограничения в xib между вашим представлением и безопасной областью для iOS 11. Назначьте верхнему ограничению приоритет 750.

В вашем контроллере представления добавьте свойство:

@property (nonatomic, strong) NSLayoutConstraint *topLayoutConstraint;

А затем в viewDidLayoutSubviews:

- (void)viewDidLayoutSubviews {
    [super viewDidLayoutSubviews];

    if (@available(iOS 11, *)) {
        // safe area constraints already set
    }
    else {
        if (!self.topLayoutConstraint) {
            self.topLayoutConstraint = [self.<yourview>.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor];
            [self.topLayoutConstraint setActive:YES];
        }
    }
}

Новое ограничение будет создано только для iOS 9 и iOS 10, имеет приоритет по умолчанию 1000 и переопределяет приоритет в xib.

Повторите для нижнего ограничения, если вам нужно избежать индикатора дома.

Версия Swift 4:

override func viewDidLayoutSubviews() {
    super.viewDidLayoutSubviews()

    if #available(iOS 11, *) {
        // safe area constraints already set
    } else {
        if topLayoutConstraint == nil {
            topLayoutConstraint = <yourview>.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor)
            topLayoutConstraint?.isActive = true
        }
    }
}
 Dobster03 февр. 2019 г., 05:49
@ user2378197 Потому что может быть код макета для <yourview> вlayoutSubviews это должно идти первым. думаюviewDidLoad также хорошо в зависимости от того, что вы делаете.
 user237819709 янв. 2019 г., 18:26
Благодарю. Почему ты положил это вviewDidLayoutSubviews вместоviewDidLoad?
 Politta04 мар. 2018 г., 23:35
@RawMean#available(iOS 11, *) проверим на iOS 11 и новее
 RawMean03 мар. 2018 г., 03:19
Будет ли это работать под iOS 12?
 iVentis03 сент. 2018 г., 10:15
Спасибо за отличное объяснение! Вдохновленный вашими идеями, я создал подклассNSLayoutConstraint что делает его более удобным в использовании, потому что ничего не должно быть реализовано в каждом viewcontroller, где вы можете использовать этот xib. это в самом конце этого поста:stackoverflow.com/a/52145680/4181169

Ваш ответ на вопрос