ฉันจะใช้ Safe Area Layout โดยใช้โปรแกรมได้อย่างไร


105

เนื่องจากฉันไม่ได้ใช้สตอรีบอร์ดเพื่อสร้างมุมมองของฉันฉันจึงสงสัยว่ามีตัวเลือก "ใช้คำแนะนำพื้นที่ปลอดภัย" แบบเป็นโปรแกรมหรืออะไรทำนองนั้น

ฉันพยายามยึดมุมมองของฉันไว้ที่

view.safeAreaLayoutGuide

แต่พวกมันยังคงทับซ้อนกับรอยบนสุดในเครื่องจำลอง iPhone X


1
ไม่มีเอกสารคุณสมบัติบูลเกี่ยวกับเรื่องนี้ตาม: developer.apple.com/documentation/uikit/uiview/…
dvp.petrov

แล้วไงview.safeAreaInsets? ลองทำดูไหม
Karthikeyan Bose

@KarthikeyanBose ใช่ฉันโชคไม่ดี
Phillip

เหมาะกับฉัน โค้ดมีลักษณะอย่างไร
Daniel Springer

คำตอบ:


163

นี่คือโค้ดตัวอย่าง (อ้างอิงจาก: Safe Area Layout Guide ):
หากคุณสร้างข้อ จำกัด ของคุณในโค้ดให้ใช้คุณสมบัติ 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 ขึ้นไปคุณจะต้องตัดข้อ จำกัด ของคำแนะนำการจัดวางพื้นที่ปลอดภัยด้วย # พร้อมใช้งานและถอยกลับไปที่คำแนะนำการจัดวางด้านบนและด้านล่างสำหรับ 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 สำหรับคู่มือการจัดวางพื้นที่ปลอดภัย


ต้องใช้ Safe Area เพื่อจัดการกับการออกแบบส่วนต่อประสานผู้ใช้สำหรับ iPhone-X นี่คือแนวทางพื้นฐานสำหรับวิธีออกแบบส่วนติดต่อผู้ใช้สำหรับ iPhone-X โดยใช้ Safe Area Layout


2
เป็นไปได้ไหมที่จะให้สิ่งนี้ในวัตถุประสงค์ -C ด้วย ดูเหมือนว่าฉันต้องการอะไร
Tom Hammond

6
@TomHammond นี่อยู่ใน Objective-C สำหรับคุณstackoverflow.com/a/47076040/5638630
Krunal

2
@ZonilyJame Now เกี่ยวกับคำถามของคุณ - SaFeAreaLayout เป็นเฟรมเวิร์กเฉพาะของ iOS (ไม่ใช่เฉพาะ iPhoneX ของอุปกรณ์) มันกำลังแทนที่คำแนะนำเค้าโครงบน - ล่างใน iOS 11 ด้วยเหตุนี้เราจึงต้องใช้ / ตั้งเงื่อนไขสำหรับ iOS ไม่ใช่สำหรับอุปกรณ์ SafeAreaLayout ดูแลการออกแบบสำหรับอุปกรณ์ทุกประเภท (iPhone-X และอื่น ๆ ) คุณสามารถขอรายละเอียดเพิ่มเติมได้หากคุณยังมีข้อสงสัย / สับสน
Krunal

3
น่ากลัว ขอบคุณ :)
ราชโมฮั

1
ทำไมจึงยอมรับว่าเป็นคำตอบที่ถูกต้อง? ฉันพยายามตั้งค่าตำแหน่งที่เป็นรูปธรรม - ผลลัพธ์เป็นแบบสุ่ม - การควบคุมถูกวางไว้ในตำแหน่งที่คาดเดาไม่ได้
Vyachaslav Gerchicov

86

ฉันกำลังใช้ส่วนขยายสำหรับมันและควบคุมว่าเป็น 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

1
นี่เป็นวิธีง่ายๆ แต่ทำได้ดี! หมายเหตุการใช้self.safeAreaLayoutGuideแทนself.layoutMarginsGuide. ตู้เซฟที่ใช้ในคำตอบนี้ทำงานได้อย่างถูกต้องเพื่อให้ฉันอยู่ในพื้นที่ปลอดภัย! สิ่งหนึ่งที่ฉันอยากจะแนะนำให้เปลี่ยนคือการใช้leadingAnchorและtrailingAnchorแทนที่จะเป็นleftAnchorและrightAnchorและไชโย!
Pranoy C

22

SafeAreaLayoutGuideเป็นUIViewทรัพย์สิน

ด้านบนของ safeAreaLayoutGuide ระบุขอบด้านบนของมุมมองที่ไม่มีการปิดบัง (เช่นไม่อยู่ด้านหลังแถบสถานะหรือแถบนำทางหากมี) ในทำนองเดียวกันสำหรับขอบอื่น ๆ

ใช้safeAreaLayoutGuideเพื่อหลีกเลี่ยงการตัด / ทับวัตถุของเราจากมุมโค้งมนแถบนำทางแถบแท็บแถบเครื่องมือและมุมมองบรรพบุรุษอื่น ๆ

เราสามารถสร้างsafeAreaLayoutGuideวัตถุและกำหนดข้อ จำกัด ของวัตถุตามลำดับ

ข้อ จำกัด สำหรับ Portrait + Landscape คือ -

ภาพบุคคล

ภาพทิวทัศน์

        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ถูกเรียกหลายครั้งดังนั้นข้อ จำกัด ของคุณจะซ้ำกันทุกครั้งที่มีการเรียก
Yevhen Dubinin

ใช่ คำตอบที่แก้ไขคุณสามารถใช้เป็นกรณีการใช้งานของคุณได้
แจ็ค

15

สำหรับผู้ที่ใช้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)}
Don Miguel

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


ตรวจสอบว่าคุณได้เพิ่มมุมมองของคุณลงใน superView แล้ว เป็น self.view ในรหัสของฉันเป็นตัวอย่างง่ายๆ
Tony TRAN

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
Jonathan Cabrera

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

การขยายพื้นที่ปลอดภัยสำหรับวัตถุประสงค์ -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) ผลลัพธ์จะไม่สนใจแถบสถานะ
Vyachaslav Gerchicov

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 ไว้ที่ superview และ superview + 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-|
  """
...
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.