Bagaimana cara menggunakan Tata Letak Area Aman secara terprogram?

100

Karena saya tidak menggunakan papan cerita untuk membuat pandangan saya, saya bertanya-tanya apakah ada opsi "Gunakan Panduan Area Aman" secara terprogram atau semacamnya.

Saya telah mencoba untuk menambatkan pandangan saya ke

view.safeAreaLayoutGuide

tetapi mereka tetap tumpang tindih dengan kedudukan tertinggi di simulator iPhone X.

Phillip
sumber
1
Tidak ada properti bool yang terdokumentasi tentang hal ini menurut: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov
Bagaimana dengan view.safeAreaInsets? Apakah kamu sudah mencobanya?
Karthikeyan Bose
@KarthikeyanBose ya saya lakukan dengan tidak beruntung sayangnya.
Phillip
bekerja untuk saya. Seperti apa kode itu
Daniel Springer

Jawaban:

157

Berikut ini contoh kode (Ref dari: Panduan Tata Letak Area Aman ):
Jika Anda membuat batasan dalam kode, gunakan properti safeAreaLayoutGuide UIView untuk mendapatkan jangkar tata letak yang relevan. Mari buat ulang contoh Interface Builder di atas dalam kode untuk melihat tampilannya:

Dengan asumsi kita memiliki tampilan hijau sebagai properti di pengontrol tampilan kita:

private let greenView = UIView()

Kita mungkin memiliki fungsi untuk menyiapkan tampilan dan batasan yang dipanggil dari viewDidLoad:

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

Buat batasan margin depan dan belakang seperti biasa menggunakan layoutMarginsGuide dari tampilan root:

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

Sekarang, kecuali Anda menargetkan iOS 11 dan yang lebih baru, Anda harus membungkus batasan panduan tata letak area aman dengan #available dan kembali ke panduan tata letak atas dan bawah untuk versi iOS sebelumnya:

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

Hasil:

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini


Berikut adalah Dokumentasi Resmi Pengembang Apple untuk Panduan Tata Letak Area Aman


Area Aman diperlukan untuk menangani desain antarmuka pengguna untuk iPhone-X. Berikut adalah pedoman dasar untuk Bagaimana merancang antarmuka pengguna untuk iPhone-X menggunakan Tata Letak Area Aman

Krunal
sumber
2
Apakah mungkin untuk memberikan ini di objektif-C juga? Sepertinya yang saya butuhkan
Tom Hammond
6
@TomHammond Ini di Objective-C untuk Anda stackoverflow.com/a/47076040/5638630
Krunal
2
@ZonilyJame Sekarang tentang permintaan Anda - SaFeAreaLayout adalah kerangka kerja khusus iOS (bukan perangkat khusus iPhoneX), Ini menggantikan panduan Tata Letak Atas-Bawah di iOS 11 sehingga kita harus menggunakan / mengatur kondisi untuk iOS bukan untuk perangkat. SafeAreaLayout menangani desain untuk semua jenis perangkat (iPhone-X dan lainnya). Anda dapat bertanya kepada saya untuk lebih jelasnya, jika Anda masih memiliki pertanyaan / kebingungan.
Krunal
3
Hebat. Terima kasih :)
Rajamohan S
1
mengapa itu diterima sebagai jawaban yang benar? Saya mencoba mengatur posisi konkret - hasilnya sepenuhnya acak - kontrol ditempatkan pada posisi yang tidak dapat diprediksi!
Vyachaslav Gerchicov
83

Saya sebenarnya menggunakan ekstensi untuk itu dan mengontrol apakah ios 11 atau tidak.

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
sumber
Ini cara sederhana untuk pergi dan melakukan trik. Terima kasih untuk idenya.
alper_k
Ini adalah cara yang sederhana namun bagus untuk melakukannya! Perhatikan penggunaan self.safeAreaLayoutGuidealih-alih self.layoutMarginsGuide. Yang aman yang digunakan dalam jawaban ini bekerja dengan benar agar saya tetap berada di area aman! Satu hal yang saya sarankan untuk diubah adalah menggunakan leadingAnchordan trailingAnchorbukannya leftAnchordan rightAnchor. Bravo!
Pranoy C
22

SafeAreaLayoutGuideadalah UIViewproperti,

Bagian atas safeAreaLayoutGuide menunjukkan tepi atas tampilan yang tidak tersamarkan (misalnya, tidak di belakang bilah status atau bilah navigasi, jika ada). Begitu pula untuk sisi-sisi lainnya.

Gunakan safeAreaLayoutGuideuntuk menghindari objek kami terpotong / tumpang tindih dari sudut membulat, bilah navigasi, bilah tab, bilah alat, dan tampilan leluhur lainnya.

Kita dapat membuat safeAreaLayoutGuideobjek & mengatur batasan objek masing-masing.

Batasan untuk Potret + Lanskap adalah -

Gambar potret

Gambar lanskap

        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

Mendongkrak
sumber
2
Jangan pernah melakukan batasan pengaturan di viewDidAppear, kecuali Anda benar-benar tahu apa yang Anda lakukan. viewDidAppeardipanggil beberapa kali dan karenanya, batasan Anda akan diduplikasi setiap kali dipanggil.
Yevhen Dubinin
Iya! , Jawaban yang diedit, dapat Anda gunakan sebagai kasus penggunaan Anda.
Jack
14

Bagi Anda yang menggunakan SnapKit , sama seperti saya, solusinya adalah menambatkan kendala Anda untuk view.safeAreaLayoutGuidemenyukai:

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()
     }
}
Phillip
sumber
1
Jawaban yang bagus. Untuk membuatnya lebih ringkas, Anda dapat mengatakan: if #available (iOS 11.0, *) {make.edges.equalTo (view.safeAreaLayoutGuide.snp.margins)}
Don Miguel
7

Saya menggunakan ini alih-alih menambahkan batasan margin depan dan belakang ke 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]
                                          ]];

Silakan juga periksa opsi untuk versi lebih rendah dari ios 11 dari jawaban Krunal.

Tony TRAN
sumber
Pastikan Anda sudah menambahkan YourView ke superView. Ini adalah self.view Dalam kode saya sebagai contoh sederhana.
Tony TRAN
6

Gunakan UIWindowatau 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]
Warif Akhand Rishi
sumber
3
Ini adalah cara untuk pergi jika tampilan Anda tidak menggunakan AutoLayout.
Jonathan Cabrera
4

Gunakan batasan dengan format visual dan Anda mendapatkan rasa hormat untuk area aman secara gratis.

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

hasil

AtomicBoolean
sumber
2
Tolong berikan komentar saat down-voting, agar kita semua bisa belajar jika ada kasus di mana teknik ini tidak berlaku. Terima kasih!
AtomicBoolean
Ini berfungsi, saya juga suka solusi format visual! Terima kasih! Tetapi apakah ini berfungsi untuk semua versi ios?
PaFi
4

Perluasan area aman Untuk 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
Mutiara Hitam
sumber
itu tidak berfungsi (Swift 4.2, iOS 12). Hasilnya mengabaikan status bar
Vyachaslav Gerchicov
2

Swift 4.2 dan 5.0. Misalkan Anda ingin menambahkan batasan Leading, Trailing, Top dan Bottom pada viewBg. Jadi, Anda dapat menggunakan kode di bawah ini.

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
Gurjinder Singh
sumber
1

Ekstensi ini membantu Anda membatasi UIVIew ke superview dan superview + safeArea-nya:

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
    }

}
Bukit Reimond
sumber
0

Anda dapat menggunakan view.safeAreaInsets seperti yang dijelaskan di sini https://www.raywenderlich.com/174078/auto-layout-visual-format-language-tutorial-2

contoh kode (diambil dari 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-|
  """
...
Serg
sumber