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

Руководства по планированию безопасной области в файлах xib - iOS 10

Я начал адаптировать свое приложение для iPhone X и нашел проблему в Interface Builder. Согласно официальным видео Apple, руководство по планированию безопасной зоны должно быть обратно совместимо. Я обнаружил, что он отлично работает в раскадровки.

Но в моих XIB файлах руководства по безопасному расположению области не соблюдаются в iOS 10.

Они отлично работают для новой версии ОС, но устройства iOS 10, похоже, просто предполагают, что безопасное расстояние области равно нулю (игнорируя размер строки состояния).

Не хватает ли какой-либо необходимой конфигурации? Это ошибка Xcode, и если да, то какие-либо известные обходные пути?

Вот скриншот проблемы в тестовом проекте (слева iOS 10, справа iOS 11):

безопасное выравнивание области в верхней части экрана

4b9b3361

Ответ 1

Есть некоторые проблемы с безопасным расположением области и обратной совместимостью. Смотрите мой комментарий здесь.

Возможно, вы сможете обойти проблемы с дополнительными ограничениями, такими как приоритет 1000 >= 20.0 для superview.top и 750 priority == safearea.top. Если вы всегда показываете строку состояния, это должно исправить ситуацию.

Лучший подход может заключаться в том, чтобы иметь отдельные раскадровки /xibs для pre-iOS 11 и iOS-11 и выше, особенно если вы столкнулись с большим количеством проблем, чем это. Причина, по которой это предпочтительнее, заключается в том, что для pre-iOS 11 вам нужно ограничивать макеты в руководстве по макете вверх/вниз, но для iOS 11 вы должны выложить их в безопасные зоны. Гиды макетов исчезли. Укладка макетов для pre-iOS 11 стилистически лучше, чем просто смещение на минимум 20 пикселей, хотя результаты будут тем же самым IFF, что всегда отображается строка состояния.

Если вы примете этот подход, вам необходимо установить для каждого файла правильную цель развертывания, в которой он будет использоваться (iOS 11 или что-то еще), чтобы Xcode не выдавал вам предупреждения и не позволял вам использовать руководства по планировке или безопасные зоны, в зависимости. В своем коде проверьте iOS 11 во время выполнения, а затем загрузите соответствующие раскадровки /xibs.

Недостатком этого подхода является поддержание (у вас есть два набора контроллеров вашего вида для поддержки и синхронизации), но как только ваше приложение поддерживает только iOS 11+ или Apple исправляет создание ограничений ограничения совместимости макета, вы можете избавиться от версий pre-iOS 11.

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

Ответ 2

В настоящее время обратная совместимость не работает.

Мое решение состоит в том, чтобы создать 2 ограничения в построителе интерфейса и удалить их в зависимости от используемой версии ios:

  • для ios 11: view.top == safe area.top
  • для более ранних версий: view.top == superview.top + 20

Добавьте их как в качестве выходов, как myConstraintSAFEAREA и myConstraintSUPERVIEW соответственно. Затем:

override func viewDidLoad() {
    if #available(iOS 11.0, *) {
        view.removeConstraint(myConstraintSUPERVIEW)
    } else {
        view.removeConstraint(myConstraintSAFEAREA)
    }
}

Ответ 3

Для меня простое исправление для работы над обеими версиями было

    if #available(iOS 11, *) {}
    else {
        self.edgesForExtendedLayout = []
    }

Из документации: "В iOS 10 и более ранних версиях используйте это свойство, чтобы сообщить, какие края вашего контроллера представлений расширяются под навигационными панелями или другими представлениями, предоставленными системой". Поэтому, устанавливая их в пустой массив, убедитесь, что контроллер просмотра не распространяется под навигационными барами.

Документ доступен здесь

Ответ 4

Я объединил некоторые ответы с этой страницы на эту тему, которая работает как прелесть (только для верхнего руководства по макету, как указано в вопросе):

  1. Обязательно используйте безопасную область в своем раскадровке или файле xib
  2. Ограничьте свои взгляды на безопасные районы
  3. Для каждой view которая имеет constraint связанное с SafeArea.top
    • Создание IBOutlet для view
    • Создайте IBOutler для constraint
  4. Внутри ViewController на viewDidLoad:

    if (@available(iOS 11.0, *)) {}
    else {
        // For each view and constraint do:
        [self.view.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
        self.constraint.active = NO;
    }
    

Редактировать:

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

@property (strong, nonatomic) IBOutletCollection(NSLayoutConstraint) NSArray *constraintsAttachedToSafeAreaTop;
@property (strong, nonatomic) IBOutletCollection(UIView) NSArray *viewsAttachedToSafeAreaTop;


if (@available(iOS 11.0, *)) {}
else {
    for (UIView *viewAttachedToSafeAreaTop in self.viewsAttachedToSafeAreaTop) {
        [viewAttachedToSafeAreaTop.topAnchor constraintEqualToAnchor:self.topLayoutGuide.bottomAnchor].active = YES;
    }
    for (NSLayoutConstraint *constraintAttachedToSafeAreaTop in self.constraintsAttachedToSafeAreaTop) {
        constraintAttachedToSafeAreaTop.active = NO;
    }
}

Счет каждого IBOutletCollection должен быть равен. например, для каждого представления должно быть связанное с ним ограничение

Ответ 5

Я закончил удаление ограничения в безопасную область, которая была у меня в моем файле xib. Вместо этого я сделал выход к рассматриваемому UIView, и из кода я подключил его так: viewDidLayoutSubviews.

let constraint = alert.viewContents.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 0)
constraint.priority = 998
constraint.isActive = true

Это связывает небольшое "предупреждение" с верхней частью экрана, но гарантирует, что просмотр содержимого внутри предупреждения всегда находится ниже верхней безопасной зоны (iOS11ish)/topLayoutGuide (iOS10ish)

Простое и одноразовое решение. Если что-то сломается, я вернусь.

Ответ 6

Это также работает:

override func viewDidLoad() {
    super.viewDidLoad()

    if #available(iOS 11.0, *) {}
    else {
        view.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height - 80).isActive = true
        view.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width - 20).isActive = true
    }
}

Ответ 7

Я добавил подкласс NSLayoutConstraint для исправления этой проблемы (IBAdjustableConstraint) с переменной @IBInspectable, выглядит так.

class IBAdjustableConstraint: NSLayoutConstraint {

    @IBInspectable var safeAreaAdjustedConstant: CGFloat = 0 {
        didSet {
            if OS.TenOrBelow {
                constant += safeAreaAdjustedConstantLegacy
            }
        }
    }
}

И OS.TenOrBelow

struct OS {
    static let TenOrBelow = UIDevice.current.systemVersion.compare("10.9", options: NSString.CompareOptions.numeric) == ComparisonResult.orderedAscending
}

Просто установите это как подкласс вашего ограничения в IB, и вы сможете сделать <iOS11 конкретные изменения. Надеюсь, это поможет кому-то.

Ответ 8

Я использовал этот вариант, добавляю верхнюю безопасную область и подключаюсь к розетке

@IBOutlet weak var topConstraint : NSLayoutConstraint!

override func viewDidLoad() {
    super.viewDidLoad()
    if !DeviceType.IS_IPHONE_X {
        if #available(iOS 11, *)  {
        }
        else{
            topConstraint.constant = 20
        }
    }
}

Ответ 9

Нашел самое простое решение - просто отключите safe area и используйте topLayoutGuide и bottomLayoutGuide + добавить исправления для iPhone X. Возможно, это не красивое решение, но требует как можно меньше усилий