Як програматично використовувати безпечне розташування зони?


99

Оскільки я не використовую розкадровки для створення своїх поглядів, мені було цікаво, чи є параметр "Використовувати путівники безпечної зони" програмно чи щось подібне.

Я намагався закріпити свої погляди

view.safeAreaLayoutGuide

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


1
Немає задокументованих властивостей bool щодо цього згідно: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov

Як щодо view.safeAreaInsets? Ви спробували це?
Karthikeyan Bose

@KarthikeyanBose, так, я, на жаль, не пощастило.
Філіп

працює для мене. Як виглядає код
Деніел Спрінгер,

Відповіді:


155

Ось зразок коду (Посилання з: Посібник із макетування безпечної зони ):
Якщо ви створюєте свої обмеження в коді, використовуйте властивість 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 за допомогою макета Safe Area


2
Чи можна було б це подати і в об’єкті-С? Це схоже саме на те, що мені потрібно
Том Хаммонд

6
@TomHammond Ось в Objective-C для вас stackoverflow.com/a/47076040/5638630
Krunal

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

3
Дивовижно. Дякую :)
Раджамохан S

1
чому це прийнято як правильну відповідь? Я намагався встановити конкретну позицію - результат повністю випадковий - елемент управління розміщений у непередбачуваному положенні!
В’ячаслав Герчиков

82

Я фактично використовую для нього розширення та контролюю, чи це 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 у відповіді Krunal.


Переконайтеся, що ви вже додали вашView до superView. Це self.view У моєму коді як простий приклад.
Tony TRAN

6

Використовуйте UIWindowабо UIViewssafeAreaInsets .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

Свіфт 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.