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



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



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



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



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



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



safe area alignment on top of the screen

726   9  

9 ответов:

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

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

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

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

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

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

в настоящее время, обратная совместимость не работает хорошо.

мое решение-создать 2 ограничения в interface builder и удалить одно в зависимости от используемой версии 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)
    }
}

для меня, простое исправление для получения его для работы на обеих версиях было

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

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

Docu доступен здесь

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

  1. обязательно используйте безопасную область в раскадровке или xib-файле
  2. ограничьте свои взгляды на безопасные области
  3. для каждого view С constraint прилагается к сейфу.верхний
    • создать IBOutlet для view
    • создать IBOutler для constraint
  4. внутри ViewController on 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;
    }
    

Edit:

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

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

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

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

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

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

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
    }
}

я добавил подкласс 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, и вы сможете внести

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

@IBOutlet weak var topConstraint : NSLayoutConstraint!

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

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

Comments

    Ничего не найдено.