Как программно использовать макет безопасной зоны?


100

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

Я пытался привязать свои взгляды к

view.safeAreaLayoutGuide

но они продолжают перекрывать высшую ступень в симуляторе iPhone X.


1
Нет задокументированного свойства bool по этому поводу согласно: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov

Как насчет view.safeAreaInsets? Вы пробовали это?
Картикеян Бозе

@KarthikeyanBose да, к сожалению, мне не повезло.
Филип

работает для меня. Как выглядит код
Дэниел Спрингер,

Ответы:


157

Вот пример кода (Ссылка из: Руководство по макету безопасной области ):
Если вы создаете ограничения в коде, используйте свойство safeAreaLayoutGuide UIView, чтобы получить соответствующие привязки макета. Давайте воссоздадим приведенный выше пример Interface Builder в коде, чтобы увидеть, как он выглядит:

Предполагая, что у нас есть зеленое представление как свойство в нашем контроллере представления:

private let greenView = UIView()

У нас может быть функция для настройки представлений и ограничений, вызываемая из viewDidLoad:

private func setupView() {
  greenView.translatesAutoresizingMaskIntoConstraints = false
  greenView.backgroundColor = .green
  view.addSubview(greenView)
}

Создайте ограничения начального и конечного поля, как всегда, используя layoutMarginsGuide корневого представления:

 let margins = view.layoutMarginsGuide
 NSLayoutConstraint.activate([
    greenView.leadingAnchor.constraint(equalTo: margins.leadingAnchor),
    greenView.trailingAnchor.constraint(equalTo: margins.trailingAnchor)
 ])

Теперь, если вы не ориентируетесь на iOS 11 и более поздние версии, вам нужно будет обернуть ограничения руководства по макету безопасной области с помощью #available и вернуться к верхним и нижним руководствам по макету для более ранних версий iOS:

if #available(iOS 11, *) {
  let guide = view.safeAreaLayoutGuide
  NSLayoutConstraint.activate([
   greenView.topAnchor.constraintEqualToSystemSpacingBelow(guide.topAnchor, multiplier: 1.0),
   guide.bottomAnchor.constraintEqualToSystemSpacingBelow(greenView.bottomAnchor, multiplier: 1.0)
   ])
} else {
   let standardSpacing: CGFloat = 8.0
   NSLayoutConstraint.activate([
   greenView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: standardSpacing),
   bottomLayoutGuide.topAnchor.constraint(equalTo: greenView.bottomAnchor, constant: standardSpacing)
   ])
}

Результат:

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

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


Вот официальная документация разработчика Apple для руководства по макету безопасной зоны.


Безопасная зона требуется для разработки пользовательского интерфейса iPhone-X. Вот базовое руководство по разработке пользовательского интерфейса для iPhone-X с использованием макета безопасной зоны.


2
Можно ли дать это и в объекте C? Похоже, это именно то, что мне нужно
Том Хэммонд

6
@TomHammond Вот в Objective-C для вас stackoverflow.com/a/47076040/5638630
Крунал

2
@ZonilyJame Теперь о вашем запросе - SaFeAreaLayout - это платформа для iOS (не для устройства iPhoneX), она заменяет руководство по макету сверху-снизу в iOS 11, поэтому мы должны использовать / устанавливать условие для iOS, а не для устройства. SafeAreaLayout заботится о дизайне для всех типов устройств (iPhone-X и другие). Вы можете спросить меня для более подробной информации, если у вас все еще есть вопросы / недоразумения.
Krunal

3
Потрясающие. Спасибо :)
Rajamohan S

1
почему это принято как правильный ответ? Я попытался установить конкретное положение - результат полностью случайный - элемент управления находится в непредсказуемой позиции!
Вячеслав Герчиков

83

Я на самом деле использую для него расширение и контролирую, ios 11 это или нет.

extension UIView {

  var safeTopAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.topAnchor
    }
    return self.topAnchor
  }

  var safeLeftAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return self.safeAreaLayoutGuide.leftAnchor
    }
    return self.leftAnchor
  }

  var safeRightAnchor: NSLayoutXAxisAnchor {
    if #available(iOS 11.0, *){
      return self.safeAreaLayoutGuide.rightAnchor
    }
    return self.rightAnchor
  }

  var safeBottomAnchor: NSLayoutYAxisAnchor {
    if #available(iOS 11.0, *) {
      return self.safeAreaLayoutGuide.bottomAnchor
    }
    return self.bottomAnchor
  }
}

Это такой простой способ сделать свое дело. Спасибо за идею.
alper_k

Это такой простой, но приятный способ сделать это! Обратите внимание на использование self.safeAreaLayoutGuideвместо self.layoutMarginsGuide. Безопасный, использованный в этом ответе, работал правильно, чтобы я оставался в безопасной зоне! Одна вещь, которую я бы предложил изменить, - это использовать leadingAnchorи trailingAnchorвместо leftAnchorи rightAnchor. Браво!
Pranoy C

22

SafeAreaLayoutGuideэто UIViewсобственность,

Верх safeAreaLayoutGuide указывает незатененный верхний край представления (например, не за строкой состояния или панелью навигации, если она есть). Аналогично для остальных краев.

Используйте, safeAreaLayoutGuideчтобы избежать отсечения / наложения наших объектов из закругленных углов, панелей навигации, панелей вкладок, панелей инструментов и других предков.

Мы можем создать safeAreaLayoutGuideобъект и установить объектные ограничения соответственно.

Ограничения для портрета + ландшафта -

Портретное изображение

Пейзажное изображение

        self.edgesForExtendedLayout = []//Optional our as per your view ladder

        let newView = UIView()
        newView.backgroundColor = .red
        self.view.addSubview(newView)
        newView.translatesAutoresizingMaskIntoConstraints = false
        if #available(iOS 11.0, *) {
            let guide = self.view.safeAreaLayoutGuide
            newView.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
            newView.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
            newView.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
            newView.heightAnchor.constraint(equalToConstant: 100).isActive = true

        }
        else {
            NSLayoutConstraint(item: newView, attribute: .top, relatedBy: .equal, toItem: view, attribute: .top, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: newView, attribute: .leading, relatedBy: .equal, toItem: view, attribute: .leading, multiplier: 1.0, constant: 0).isActive = true
            NSLayoutConstraint(item: newView, attribute: .trailing, relatedBy: .equal, toItem: view, attribute: .trailing, multiplier: 1.0, constant: 0).isActive = true

            newView.heightAnchor.constraint(equalToConstant: 100).isActive = true
        }

UILayoutGuide

safeAreaLayoutGuide


2
Никогда не устанавливайте ограничения на установку viewDidAppear, если вы не знаете, что делаете. viewDidAppearвызывается несколько раз, поэтому ваши ограничения будут дублироваться каждый раз при его вызове.
Евгений Дубинин

Да! , Отредактированный ответ, вы можете использовать как свой вариант использования.
Джек

14

Для тех из вас, кто использует SnapKit , как и для меня, решение заключается в привязке ваших ограничений к следующему view.safeAreaLayoutGuide:

yourView.snp.makeConstraints { (make) in
    if #available(iOS 11.0, *) {
        //Bottom guide
        make.bottom.equalTo(view.safeAreaLayoutGuide.snp.bottomMargin)
        //Top guide
        make.top.equalTo(view.safeAreaLayoutGuide.snp.topMargin)
        //Leading guide
        make.leading.equalTo(view.safeAreaLayoutGuide.snp.leadingMargin)
        //Trailing guide
        make.trailing.equalTo(view.safeAreaLayoutGuide.snp.trailingMargin)

     } else {
        make.edges.equalToSuperview()
     }
}

1
Отличный ответ. Чтобы сделать его более компактным, можно сказать: if #available (iOS 11.0, *) {make.edges.equalTo (view.safeAreaLayoutGuide.snp.margins)}
Дон Мигель

7

Я использую это вместо добавления начальных и конечных ограничений маржи в layoutMarginsGuide:

UILayoutGuide *safe = self.view.safeAreaLayoutGuide;
yourView.translatesAutoresizingMaskIntoConstraints = NO;
[NSLayoutConstraint activateConstraints:@[
                                           [safe.trailingAnchor constraintEqualToAnchor:yourView.trailingAnchor],
                                           [yourView.leadingAnchor constraintEqualToAnchor:safe.leadingAnchor],
                                           [yourView.topAnchor constraintEqualToAnchor:safe.topAnchor],
                                           [safe.bottomAnchor constraintEqualToAnchor:yourView.bottomAnchor]
                                          ]];

Также проверьте вариант для более низкой версии ios 11 из ответа Крунала.


Убедитесь, что вы уже добавили yourView в superView. Это self.view В моем коде в качестве простого примера.
Тони

6

Использование UIWindowили UIView«ssafeAreaInsets .bottom .top .left .right

// #available(iOS 11.0, *)
// height - UIApplication.shared.keyWindow!.safeAreaInsets.bottom

// On iPhoneX
// UIApplication.shared.keyWindow!.safeAreaInsets.top =  44
// UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 34

// Other devices
// UIApplication.shared.keyWindow!.safeAreaInsets.top =  0
// UIApplication.shared.keyWindow!.safeAreaInsets.bottom = 0

// example
let window = UIApplication.shared.keyWindow!
let viewWidth = window.frame.size.width
let viewHeight = window.frame.size.height - window.safeAreaInsets.bottom
let viewFrame = CGRect(x: 0, y: 0, width: viewWidth, height: viewHeight)
let aView = UIView(frame: viewFrame)
aView.backgroundColor = .red
view.addSubview(aView)
aView.autoresizingMask = [.flexibleWidth, .flexibleHeight]

3
Это правильный вариант, если ваше представление не использует AutoLayout.
Джонатан Кабрера,

4

Используйте ограничения с визуальным форматом, и вы получите уважение к безопасной зоне бесплатно.

class ViewController: UIViewController {

    var greenView = UIView()

    override func viewDidLoad() {
        super.viewDidLoad()
        greenView.backgroundColor = .green
        view.addSubview(greenView)
    }
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()

        greenView.translatesAutoresizingMaskIntoConstraints = false
        let views : [String:Any] = ["greenView":greenView]
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "H:|-[greenView]-|", options: [], metrics: nil, views: views))
        view.addConstraints(NSLayoutConstraint.constraints(withVisualFormat: "V:|-[greenView]-|", options: [], metrics: nil, views: views))
    }
}

результат


2
Пожалуйста, оставьте комментарий при отрицательном голосовании, чтобы мы все могли узнать, есть ли случай, когда этот метод неприменим. Спасибо!
AtomicBoolean

Это работает, мне также нравятся решения визуального формата! Спасибо! Но работает ли это на всех версиях ios?
PaFi

4

Расширение безопасной зоны для Objective-C

@implementation UIView (SafeArea)

- (NSLayoutAnchor *)safeTopAnchor{

    if (@available(iOS 11.0, *)){
        return self.safeAreaLayoutGuide.topAnchor;
    } else {
        return self.topAnchor;
    }

}


- (NSLayoutAnchor *)safeBottomAnchor{

    if (@available(iOS 11.0, *)) {
        return self.safeAreaLayoutGuide.bottomAnchor;
    } else {
        return self.bottomAnchor;
    }

}

@end

не работает (Swift 4.2, iOS 12). Результат игнорирует строку состояния
Вячеслав Герчиков

2

Swift 4.2 и 5.0. Предположим, вы хотите добавить ограничения Leading, Trailing, Top и Bottom для viewBg. Итак, вы можете использовать приведенный ниже код.

let guide = self.view.safeAreaLayoutGuide
viewBg.trailingAnchor.constraint(equalTo: guide.trailingAnchor).isActive = true
viewBg.leadingAnchor.constraint(equalTo: guide.leadingAnchor).isActive = true
viewBg.topAnchor.constraint(equalTo: guide.topAnchor).isActive = true
viewBg.bottomAnchor.constraint(equalTo: guide.bottomAnchor).isActive = true

1

Это расширение помогает вам ограничить UIVIew его супервизором и супервизором + safeArea:

extension UIView {

    ///Constraints a view to its superview
    func constraintToSuperView() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.rightAnchor).isActive = true
    }

    ///Constraints a view to its superview safe area
    func constraintToSafeArea() {
        guard let superview = superview else { return }
        translatesAutoresizingMaskIntoConstraints = false

        topAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.topAnchor).isActive = true
        leftAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.leftAnchor).isActive = true
        bottomAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.bottomAnchor).isActive = true
        rightAnchor.constraint(equalTo: superview.safeAreaLayoutGuide.rightAnchor).isActive = true
    }

}

0

Вы можете использовать view.safeAreaInsets, как описано здесь https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

пример кода (взято с сайта raywenderlich.com):

override func viewSafeAreaInsetsDidChange() {
  super.viewSafeAreaInsetsDidChange()

  if !allConstraints.isEmpty {
    NSLayoutConstraint.deactivate(allConstraints)
    allConstraints.removeAll()
  }

  let newInsets = view.safeAreaInsets
  let leftMargin = newInsets.left > 0 ? newInsets.left : Metrics.padding
  let rightMargin = newInsets.right > 0 ? newInsets.right : Metrics.padding
  let topMargin = newInsets.top > 0 ? newInsets.top : Metrics.padding
  let bottomMargin = newInsets.bottom > 0 ? newInsets.bottom : Metrics.padding

  let metrics = [
    "horizontalPadding": Metrics.padding,
    "iconImageViewWidth": Metrics.iconImageViewWidth,
    "topMargin": topMargin,
    "bottomMargin": bottomMargin,
    "leftMargin": leftMargin,
    "rightMargin": rightMargin]
}


let views: [String: Any] = [
  "iconImageView": iconImageView,
  "appNameLabel": appNameLabel,
  "skipButton": skipButton,
  "appImageView": appImageView,
  "welcomeLabel": welcomeLabel,
  "summaryLabel": summaryLabel,
  "pageControl": pageControl]

let iconVerticalConstraints = NSLayoutConstraint.constraints(
  withVisualFormat: "V:|-topMargin-[iconImageView(30)]",
  metrics: metrics,
  views: views)
allConstraints += iconVerticalConstraints

let topRowHorizontalFormat = """
  H:|-leftMargin-[iconImageView(iconImageViewWidth)]-[appNameLabel]-[skipButton]-rightMargin-|
  """
...
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.