Làm cách nào để sử dụng Bố cục Khu vực An toàn theo lập trình?


99

Vì tôi không sử dụng bảng phân cảnh để tạo chế độ xem của mình, tôi đã tự hỏi liệu có tùy chọn "Sử dụng Hướng dẫn Khu vực An toàn" theo chương trình hay tương tự không.

Tôi đã cố gắng neo quan điểm của mình vào

view.safeAreaLayoutGuide

nhưng chúng tiếp tục chồng chéo đỉnh cao trong trình mô phỏng iPhone X.


1
Không có tài sản bool tài liệu về vấn đề này theo: developer.apple.com/documentation/uikit/uiview/...
dvp.petrov

Làm thế nào về view.safeAreaInsets? Bạn đã thử cái này chưa?
Karthikeyan Bose

@KarthikeyanBose vâng, thật không may, tôi đã không gặp may.
Phillip

làm việc cho tôi. Mã trông như thế nào
Daniel Springer

Câu trả lời:


155

Đây là mã mẫu (Tham khảo từ: Hướng dẫn Bố trí Khu vực An toàn ):
Nếu bạn tạo các ràng buộc của mình trong mã, hãy sử dụng thuộc tính safeAreaLayoutGuide của UIView để nhận các neo bố cục có liên quan. Hãy tạo lại ví dụ về Trình tạo giao diện ở trên bằng mã để xem nó trông như thế nào:

Giả sử chúng ta có chế độ xem màu xanh lá cây như một thuộc tính trong bộ điều khiển chế độ xem của chúng ta:

private let greenView = UIView()

Chúng ta có thể có một hàm để thiết lập các khung nhìn và ràng buộc được gọi từ viewDidLoad:

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

Tạo các ràng buộc lề đầu và cuối như mọi khi bằng cách sử dụng layoutMarginsGuide của chế độ xem gốc:

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

Bây giờ, trừ khi bạn đang nhắm mục tiêu iOS 11 trở lên, bạn sẽ cần phải bao bọc các ràng buộc hướng dẫn bố cục khu vực an toàn bằng #available và quay lại hướng dẫn bố cục trên cùng và dưới cùng cho các phiên bản iOS trước đó:

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

Kết quả:

nhập mô tả hình ảnh ở đây

nhập mô tả hình ảnh ở đây


Đây là Tài liệu Chính thức dành cho Nhà phát triển của Apple về Hướng dẫn Bố trí Khu vực An toàn


Khu vực An toàn là bắt buộc để xử lý thiết kế giao diện người dùng cho iPhone-X. Đây là hướng dẫn cơ bản về Cách thiết kế giao diện người dùng cho iPhone-X bằng Bố cục Vùng An toàn


2
Có thể cung cấp điều này trong mục tiêu-C không? Dường như chỉ là những gì tôi cần
Tom Hammond

6
@TomHammond Đây là trong Objective-C cho bạn stackoverflow.com/a/47076040/5638630
Krunal

2
@ZonilyJame Bây giờ về truy vấn của bạn - SaFeAreaLayout là khung dành riêng cho iOS (không dành riêng cho thiết bị iPhoneX), nó sẽ thay thế hướng dẫn Bố cục trên cùng trong iOS 11, do đó chúng ta phải sử dụng / đặt điều kiện cho iOS chứ không phải cho thiết bị. SafeAreaLayout chăm sóc thiết kế cho tất cả các loại thiết bị (iPhone-X và các loại khác). Bạn có thể hỏi tôi để biết thêm chi tiết, nếu bạn vẫn còn thắc mắc / nhầm lẫn.
Krunal

3
Tuyệt vời. Cảm ơn bạn :)
Rajamohan S

1
tại sao nó được chấp nhận là câu trả lời đúng? Tôi đã cố gắng thiết lập một vị trí cụ thể - kết quả là hoàn toàn ngẫu nhiên - điều khiển được đặt ở vị trí không thể đoán trước!
Vyachaslav Gerchicov,

82

Tôi thực sự đang sử dụng một tiện ích mở rộng cho nó và kiểm soát xem nó có phải là ios 11 hay không.

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

Đó là một cách đơn giản để đi và thực hiện thủ thuật. Cảm ơn vì ý tưởng.
alper_k

Đây là một cách đơn giản nhưng tốt để làm điều đó! Lưu ý việc sử dụng self.safeAreaLayoutGuidethay vì self.layoutMarginsGuide. Két an toàn được sử dụng trong câu trả lời này đã hoạt động chính xác để tôi luôn ở trong khu vực an toàn! Một điều tôi đề nghị thay đổi sẽ là sử dụng leadingAnchortrailingAnchorthay vì leftAnchorrightAnchor. Hoan hô!
Pranoy C

22

SafeAreaLayoutGuideUIViewtài sản,

Phần trên cùng của safeAreaLayoutGuide chỉ ra cạnh trên cùng không được quan sát của chế độ xem (ví dụ: không nằm sau thanh trạng thái hoặc thanh điều hướng, nếu có). Tương tự đối với các cạnh khác.

Sử dụng safeAreaLayoutGuideđể tránh các đối tượng của chúng ta cắt / chồng lên các góc tròn, thanh điều hướng, thanh tab, thanh công cụ và các dạng xem tổ tiên khác.

Chúng ta có thể tạo safeAreaLayoutGuideđối tượng & thiết lập các ràng buộc đối tượng tương ứng.

Ràng buộc đối với Dọc + Ngang là -

Ảnh chân dung

Hình ảnh ngang

        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
Không bao giờ thực hiện các ràng buộc thiết lập trong viewDidAppear, trừ khi bạn hoàn toàn biết mình đang làm gì. viewDidAppearđược gọi nhiều lần và do đó, các ràng buộc của bạn sẽ được nhân đôi mỗi khi nó được gọi.
Yevhen Dubinin

Đúng! , Câu trả lời đã chỉnh sửa, bạn có thể sử dụng làm trường hợp sử dụng của mình.
Jack

14

Đối với những người sử dụng SnapKit , giống như tôi, giải pháp là giải quyết các ràng buộc của bạn để view.safeAreaLayoutGuidethích như vậy:

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
Câu trả lời chính xác. Để làm cho nó nhỏ gọn hơn một chút, bạn có thể nói: if #available (iOS 11.0, *) {make.edges.equalTo (view.safeAreaLayoutGuide.snp.margins)}
Don Miguel

7

Tôi đang sử dụng điều này thay vì thêm các ràng buộc lề đầu và cuối vào 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]
                                          ]];

Vui lòng kiểm tra tùy chọn cho phiên bản thấp hơn của ios 11 từ câu trả lời của Krunal.


Đảm bảo rằng bạn đã thêm Chế độ xem của mình vào SuperView. Nó là self.view Trong mã của tôi như một ví dụ đơn giản.
Tony TRAN

6

Sử dụng UIWindowhoặc 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
Đây là cách để thực hiện nếu chế độ xem của bạn không sử dụng Tự động thanh toán.
Jonathan Cabrera

4

Sử dụng các ràng buộc với định dạng trực quan và bạn được tôn trọng khu vực an toàn miễn phí.

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

kết quả


2
Vui lòng cung cấp nhận xét khi bỏ phiếu xuống để tất cả chúng ta có thể tìm hiểu nếu có trường hợp không áp dụng kỹ thuật này. Cảm ơn!
AtomicBoolean

Điều này hoạt động, tôi cũng thích các giải pháp định dạng trực quan! Cảm ơn! Nhưng điều này có hoạt động cho tất cả các phiên bản ios không?
PaFi

4

Mở rộng vùng an toàn cho 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

nó không hoạt động (Swift 4.2, iOS 12). Thanh trạng thái bỏ qua của kết quả
Vyachaslav Gerchicov

2

Swift 4.2 và 5.0. Giả sử bạn muốn thêm các ràng buộc Dẫn đầu, Theo dõi, Trên cùng và Dưới cùng trên viewBg. Vì vậy, bạn có thể sử dụng mã dưới đây.

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

Tiện ích mở rộng này giúp bạn giới hạn UIVIew với superview và superview + safe của nó

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

Bạn có thể sử dụng view.safeAreaInsets như được giải thích tại đây https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

mẫu mã (lấy từ 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-|
  """
...
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.