ลบข้อ จำกัด ทั้งหมดที่มีผลต่อ UIView


90

ฉันมี UIView ซึ่งวางอยู่บนหน้าจอผ่านข้อ จำกัด หลายประการ ข้อ จำกัด บางอย่างเป็นของ Superview ส่วนข้อ จำกัด อื่น ๆ เป็นของบรรพบุรุษคนอื่น ๆ (เช่นอาจเป็นคุณสมบัติการดูของ UIViewController)

ฉันต้องการลบข้อ จำกัด เก่า ๆ เหล่านี้ทั้งหมดและวางไว้ที่ใหม่โดยใช้ข้อ จำกัด ใหม่

ฉันจะทำสิ่งนี้ได้อย่างไรโดยไม่ต้องสร้าง IBOutlet สำหรับทุกข้อ จำกัด และต้องจำว่ามุมมองใดเป็นเจ้าของข้อ จำกัด ดังกล่าว

ในการอธิบายอย่างละเอียดวิธีการที่ไร้เดียงสาคือการสร้าง IBOutlets จำนวนมากสำหรับข้อ จำกัด แต่ละข้อจากนั้นจะเกี่ยวข้องกับการเรียกรหัสเช่น:

[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];

ปัญหาเกี่ยวกับรหัสนี้คือแม้ในกรณีที่ง่ายที่สุดฉันจะต้องสร้าง IBOutlets 2 รายการ สำหรับเลย์เอาต์ที่ซับซ้อนสิ่งนี้สามารถเข้าถึง IBOutlets ที่ต้องการได้ 4 หรือ 8 รายการ นอกจากนี้ฉันต้องตรวจสอบให้แน่ใจว่าการเรียกร้องให้ลบข้อ จำกัด นั้นถูกเรียกใช้ในมุมมองที่เหมาะสม ตัวอย่างเช่นสมมติว่าเป็นเจ้าของโดยmyViewsLeftConstraint viewAถ้าฉันเผลอโทร[self.view removeConstraint:self.myViewsLeftConstraint]ไปจะไม่มีอะไรเกิดขึ้น

หมายเหตุ: method constraintsAffectingLayoutForAxisมีแนวโน้มที่ดี แต่มีไว้สำหรับการดีบักเท่านั้น


ปรับปรุง:หลายคำตอบที่ผมได้รับข้อตกลงกับself.constraints, self.superview.constraintsหรือแตกต่างจากผู้ที่บาง โซลูชันเหล่านี้จะใช้ไม่ได้เนื่องจากวิธีการเหล่านั้นส่งคืนเฉพาะข้อ จำกัด ที่เป็นของมุมมองไม่ใช่ข้อ จำกัดที่มีผลต่อมุมมอง

เพื่อชี้แจงปัญหาด้วยวิธีแก้ปัญหาเหล่านี้ให้พิจารณาลำดับชั้นของมุมมองนี้:

  • ปู่
    • พ่อ
      • ฉัน
        • ลูกชาย
        • ลูกสาว
      • บราเดอร์
    • ลุง

ลองนึกภาพว่าเราสร้างข้อ จำกัด ต่อไปนี้และแนบไว้กับบรรพบุรุษร่วมกันที่ใกล้ที่สุด:

  • C0: ฉัน: ท็อปเดียวกับลูกชาย (เป็นของฉัน)
  • C1: ฉัน: width = 100 (เป็นของฉัน)
  • C2: ฉัน: สูงเท่าพี่ชาย (พ่อเป็นเจ้าของ)
  • C3: ฉัน: ท็อปเดียวกับลุง (ปู่เป็นเจ้าของ)
  • C4: ฉัน: เหลือเหมือนปู่ (ปู่เป็นเจ้าของ)
  • C5: พี่ชาย: เหลือคนเดียวกับพ่อ (เป็นของพ่อ)
  • C6: ลุง: ซ้ายเหมือนปู่ (ปู่เป็นเจ้าของ)
  • C7: ลูกชาย: เหลือเหมือนลูกสาว (เป็นของฉัน)

ตอนนี้คิดว่าเราต้องการที่จะลบข้อ จำกัด Meที่มีผลต่อ วิธีแก้ปัญหาที่เหมาะสมควรลบออก[C0,C1,C2,C3,C4]และไม่มีอะไรอื่น

ถ้าฉันใช้self.constraints(ที่ตัวเองคือฉัน) ฉันจะได้รับ[C0,C1,C7]เนื่องจากสิ่งเหล่านี้เป็นข้อ จำกัด เดียวที่ฉันเป็นเจ้าของ [C2,C3,C4]เห็นได้ชัดว่ามันจะไม่พอที่จะลบนี้เพราะมันจะหายไป นอกจากนี้ยังเป็นการลบC7โดยไม่จำเป็น

ถ้าฉันใช้self.superview.constraints(ที่ตัวเองคือฉัน) ฉันจะได้รับ[C2,C5]เนื่องจากสิ่งเหล่านี้เป็นข้อ จำกัด ของคุณพ่อ เห็นได้ชัดว่าเราไม่สามารถลบทั้งหมดเหล่านี้ตั้งแต่เป็นสมบูรณ์ไม่เกี่ยวข้องกับC5Me

ถ้าผมใช้ผมจะได้รับgrandfather.constraints [C3,C4,C6]อีกครั้งเราไม่สามารถลบสิ่งเหล่านี้ทั้งหมดได้เนื่องจากC6ควรจะยังคงอยู่

วิธีการบังคับแบบเดรัจฉานคือการวนซ้ำบรรพบุรุษของแต่ละมุมมอง (รวมถึงตัวมันเอง) และดูว่าfirstItemหรือsecondItemเป็นมุมมองนั้นเอง ถ้าเป็นเช่นนั้นให้ลบข้อ จำกัด นั้นออก สิ่งนี้จะนำไปสู่การแก้ปัญหาที่ถูกต้องการคืนค่า[C0,C1,C2,C3,C4]และข้อ จำกัด เหล่านั้นเท่านั้น

อย่างไรก็ตามฉันหวังว่าจะมีวิธีแก้ปัญหาที่หรูหรากว่าการวนซ้ำรายชื่อบรรพบุรุษทั้งหมด


คุณใส่ตัวระบุในข้อ จำกัด ทั้งหมดที่คุณต้องการลบได้อย่างไร? ด้วยวิธีนี้คุณไม่จำเป็นต้องมีเต้าเสียบสำหรับพวกเขา
nsuinteger

คำตอบ:


71

วิธีนี้ใช้ได้ผลสำหรับฉัน:

@interface UIView (RemoveConstraints)

- (void)removeAllConstraints;

@end


@implementation UIView (RemoveConstraints)

- (void)removeAllConstraints
{
    UIView *superview = self.superview;
    while (superview != nil) {
        for (NSLayoutConstraint *c in superview.constraints) {
            if (c.firstItem == self || c.secondItem == self) {
                [superview removeConstraint:c];
            }
        }
        superview = superview.superview;
    }

    [self removeConstraints:self.constraints];
    self.translatesAutoresizingMaskIntoConstraints = YES;
}

@end

หลังจากเสร็จสิ้นการเรียกใช้มุมมองของคุณจะยังคงอยู่ที่เดิมเนื่องจากสร้างข้อ จำกัด การปรับอัตโนมัติ เมื่อฉันไม่ทำเช่นนี้มุมมองมักจะหายไป นอกจากนี้ยังไม่เพียงแค่ลบข้อ จำกัด จาก superview เท่านั้น แต่ยังข้ามไปจนสุดเนื่องจากอาจมีข้อ จำกัด ที่ส่งผลต่อข้อ จำกัด ในมุมมองของบรรพบุรุษ


เวอร์ชัน Swift 4

extension UIView {
    
    public func removeAllConstraints() {
        var _superview = self.superview
        
        while let superview = _superview {
            for constraint in superview.constraints {
                
                if let first = constraint.firstItem as? UIView, first == self {
                    superview.removeConstraint(constraint)
                }
                
                if let second = constraint.secondItem as? UIView, second == self {
                    superview.removeConstraint(constraint)
                }
            }
            
            _superview = superview.superview
        }
        
        self.removeConstraints(self.constraints)
        self.translatesAutoresizingMaskIntoConstraints = true
    }
}

3
นี่น่าจะเป็นคำตอบอย่างเป็นทางการ
Jason Crocker

ทำไมคุณถึงมี self.translatesAutoresizingMaskIntoConstraints = ใช่; เหรอ? แท้จริงคุณไม่ต้องการสิ่งนี้สำหรับมุมมองที่คุณกำลังตั้งค่าด้วยข้อ จำกัด ?
lensovet

4
ฉันกำลังลบข้อ จำกัด ดังนั้นฉันต้องการใช้ข้อ จำกัด การปรับขนาดอัตโนมัติหลังจากที่ฉันลบข้อ จำกัด อื่น ๆ เพื่อยึดมันไว้โดยที่ไม่มีบรรทัดนั้นในโค้ดของฉันมุมมองจะหายไป
marchinram

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

C7นี้ไม่ถูกต้องจะเอา แต่ก็ควรจะง่ายต่อการแก้ไขหากคุณลบ[self removeConstraints:self.constraints];และการเปลี่ยนแปลงไปUIView *superview = self.superview; UIView *superview = self;
Senseful

50

ทางออกเดียวที่ฉันพบจนถึงตอนนี้คือการลบมุมมองจากซุปเปอร์วิว:

[view removeFromSuperview]

ดูเหมือนว่าจะลบข้อ จำกัด ทั้งหมดที่มีผลต่อเลย์เอาต์และพร้อมที่จะเพิ่มลงใน superview และมีข้อ จำกัด ใหม่ อย่างไรก็ตามจะลบมุมมองย่อยออกจากลำดับชั้นอย่าง[C7]ไม่ถูกต้องด้วยและกำจัดอย่างไม่ถูกต้อง


คุณสามารถใช้คุณสมบัติ firstItem และ secondItem ของข้อ จำกัด เพื่อตรวจสอบว่ามีผลกับมุมมองของคุณหรือไม่และวนรอบลำดับชั้นมุมมองเพื่อค้นหาทั้งหมด ฉันคิดว่าการลบและเพิ่มใหม่เป็นวิธีแก้ปัญหาที่ดีกว่า ฉันสนใจที่จะดูกรณีการใช้งานของคุณเนื่องจากดูเหมือนว่านี่เป็นสิ่งที่แย่มากที่ต้องทำ ข้อ จำกัด ของคุณอาจไม่ถูกต้องได้อย่างง่ายดายเนื่องจากมุมมองอื่น ๆ อาจอาศัยข้อ จำกัด เหล่านั้นในการจัดวางอย่างเหมาะสม
bjtitus

@bjtitus: จุดดี อย่างไรก็ตามในกรณีเฉพาะนี้ฉันกำลังเปลี่ยนตำแหน่งมุมมองที่ไม่มีอะไรขึ้นอยู่กับมัน ใช้มุมมองอื่นเพื่อให้ทราบว่าจะวางไว้ที่ใดและไม่มีมุมมองที่ใช้เพื่อให้ทราบตำแหน่งที่จะวาง
Senseful

ไม่ใช่ทางออกเดียว แต่ง่ายมาก ในกรณีการใช้งานของฉันฉันเพิ่งลบมุมมองจากซูเปอร์วิวแล้วเพิ่มใหม่ในภายหลัง
MariánČerný

49

คุณสามารถลบข้อ จำกัด ทั้งหมดในมุมมองได้โดยทำสิ่งนี้:

self.removeConstraints(self.constraints)

แก้ไข: หากต้องการลบข้อ จำกัด ของมุมมองย่อยทั้งหมดให้ใช้ส่วนขยายต่อไปนี้ใน Swift:

extension UIView {
    func clearConstraints() {
        for subview in self.subviews {
            subview.clearConstraints()
        }
        self.removeConstraints(self.constraints)
    }
}

22
ปัญหาในการแก้ปัญหานี้คือจะลบเฉพาะข้อ จำกัด ที่มุมมองนี้เป็นเจ้าของ (เช่นความกว้างและความสูง) ไม่ได้ลบสิ่งต่างๆเช่นข้อ จำกัด ด้านบนและด้านบน
Senseful

2
ฉันยังไม่ได้ยืนยันความคิดเห็นของคุณด้านบน อย่างไรก็ตามฉันเชื่อว่าสิ่งนี้จะได้ผลเหมือนหรือดีกว่าข้างบน [NSLayoutConstraint deactivateConstraints: self.constraints];
emdog4

2
ที่จะประสบปัญหาเดียวกันกับวิธีการแก้ปัญหาเดิม self.constraintsส่งกลับเฉพาะข้อ จำกัด ที่selfเป็นเจ้าของไม่ได้ทุกข้อ จำกัด selfที่มีผลต่อ
Senseful

1
ฉันเข้าใจสิ่งที่คุณกำลังพูดตอนนี้ ในคำตอบของฉันฉันกำลังลบข้อ จำกัด ออกจาก contentView ของ UITableViewCell จากนั้นวิธีการ updateConstraints จะเพิ่มข้อ จำกัด สำหรับมุมมองย่อยทั้งหมดและรีเซ็ตเค้าโครง ในความคิดเห็นแรกของฉันด้านบนฉันควรพิมพ์ self.view.constraints ทั้ง self.view และ self.contentView อยู่ที่ด้านบนสุดของลำดับชั้นมุมมอง การตั้งค่า translatesAutoresizingMasksToConstraints = ใช่บน self.view หรือ self.contentView เป็นความคิดที่ไม่ดี ;-). ฉันไม่ชอบการเรียก addSubview: ในเมธอด updateConstraints ของฉันการลบมุมมองออกจากลำดับชั้นดูเหมือนว่าไม่จำเป็น
emdog4

ฉันเพิ่งอัปเดตคำถามเพื่อแสดงว่าเหตุใดวิธีแก้ปัญหาเช่นนี้จึงใช้ไม่ได้กับสิ่งที่ฉันต้องการ สมมติว่าคุณอยู่ใน UITableViewCell เทียบเท่ากับ contentView คือปู่ ดังนั้นวิธีการแก้ปัญหาของคุณไม่ถูกต้องจะเอาและไม่ถูกต้องไม่ลบ[C6] [C0,C1,C2]
Senseful

20

มีสองวิธีในการบรรลุเป้าหมายดังกล่าวตามเอกสารสำหรับนักพัฒนาของ Apple

1. NSLayoutConstraint.deactivateConstraints

นี่เป็นวิธีอำนวยความสะดวกที่ให้วิธีง่ายๆในการปิดใช้งานชุดข้อ จำกัด ด้วยการโทรเพียงครั้งเดียว ผลของวิธีนี้เหมือนกับการตั้งค่าคุณสมบัติ isActive ของแต่ละข้อ จำกัด เป็นเท็จ โดยปกติแล้วการใช้วิธีนี้จะมีประสิทธิภาพมากกว่าการปิดใช้งานข้อ จำกัด ทีละข้อ

// Declaration
class func deactivate(_ constraints: [NSLayoutConstraint])

// Usage
NSLayoutConstraint.deactivate(yourView.constraints)

2. UIView.removeConstraints(เลิกใช้งานสำหรับ> = iOS 8.0)

เมื่อพัฒนาสำหรับ iOS 8.0 ขึ้นไปให้ใช้เมธอด deactivateConstraints ของคลาส NSLayoutConstraint แทนการเรียกเมธอด removeConstraints: โดยตรง deactivateConstraints: วิธีการลบข้อ จำกัด โดยอัตโนมัติจากมุมมองที่ถูกต้อง

// Declaration
func removeConstraints(_ constraints: [NSLayoutConstraint])`

// Usage
yourView.removeConstraints(yourView.constraints)

เคล็ดลับ

การใช้Storyboards หรือXIBs อาจเป็นความเจ็บปวดในการกำหนดค่าข้อ จำกัด ตามที่กล่าวไว้ในสถานการณ์ของคุณคุณต้องสร้าง IBOutlets สำหรับแต่ละรายการที่คุณต้องการลบ ถึงกระนั้นเวลาส่วนใหญ่ก็Interface Builderสร้างปัญหามากกว่าที่จะแก้ได้

ดังนั้นเมื่อมีเนื้อหาแบบไดนามิกมากและสถานะของมุมมองที่แตกต่างกันฉันขอแนะนำ:

  1. การสร้างมุมมองของคุณโดยใช้โปรแกรม
  2. จัดวางและใช้NSLayoutAnchor
  3. ผนวกแต่ละข้อ จำกัด ที่อาจถูกลบออกในภายหลังเข้ากับอาร์เรย์
  4. ล้างทุกครั้งก่อนใช้สถานะใหม่

รหัสง่ายๆ

private var customConstraints = [NSLayoutConstraint]()

private func activate(constraints: [NSLayoutConstraint]) {
    customConstraints.append(contentsOf: constraints)
    customConstraints.forEach { $0.isActive = true }
}

private func clearConstraints() {
    customConstraints.forEach { $0.isActive = false }
    customConstraints.removeAll()
}

private func updateViewState() {
    clearConstraints()

    let constraints = [
        view.leadingAnchor.constraint(equalTo: parentView.leadingAnchor),
        view.trailingAnchor.constraint(equalTo: parentView.trailingAnchor),
        view.topAnchor.constraint(equalTo: parentView.topAnchor),
        view.bottomAnchor.constraint(equalTo: parentView.bottomAnchor)
    ]

    activate(constraints: constraints)

    view.layoutIfNeeded()
}

อ้างอิง

  1. NSLayoutConstraint
  2. UIView

2
ระวัง. มุมมองที่ระบุขนาดเนื้อหาที่แท้จริงจะมี NSContentSizeLayoutConstraint เพิ่มโดยอัตโนมัติโดย iOS การลบสิ่งนี้โดยการเข้าถึงคุณสมบัติ yourView.constraints ปกติจะทำลายการกำหนดอัตโนมัติในมุมมองเหล่านั้น
TigerCoding

ปัญหาในการแก้ปัญหานี้คือจะลบเฉพาะข้อ จำกัด ที่มุมมองนี้เป็นเจ้าของ (เช่นความกว้างและความสูง) ไม่ได้ลบสิ่งต่างๆเช่นข้อ จำกัด ด้านบนและด้านบน
Senseful

ใช่สิ่งที่ Senseful พูด แม้ว่าจะมีวิธีการเหล่านี้ แต่ก็ไม่สามารถแก้ปัญหาที่คำถามนั้นถามได้ด้วยตัวเอง
GSnyder

18

ใน Swift:

import UIKit

extension UIView {

    /**
     Removes all constrains for this view
     */
    func removeConstraints() {

        let constraints = self.superview?.constraints.filter{
            $0.firstItem as? UIView == self || $0.secondItem as? UIView == self
        } ?? []

        self.superview?.removeConstraints(constraints)
        self.removeConstraints(self.constraints)
    }
}

การดำเนินการนี้จะผิดพลาดหากมุมมองของคุณไม่มี superview ใน "superview!" กรุณาตัดส่วนนี้เป็น 'if let superview = superview' :)
Bersaelor

1
สิ่งนี้จะไม่ลบข้อ จำกัด ทั้งหมด ข้อ จำกัด ใช้พาเรนต์ที่ใกล้ที่สุดของ 2 มุมมองที่ได้รับผลกระทบดังนั้นหากคุณสร้างข้อ จำกัด ที่ไม่แบ่งปันมุมมองที่ยอดเยี่ยมของมุมมองนี้ข้อ จำกัด นั้นจะไม่ถูกลบออก ...
vrwim

2
บางทีอาจจะต้องมากขึ้นที่จะเปรียบเทียบตัวตนif c.firstItem === selfแทนการหล่อas? UIViewและตรวจสอบเท่าเทียมกับ==
user1244109

ไม่ควรใช้ removeConstraints: from apple doc: // เมธอด removeConstraints จะเลิกใช้งานในอนาคตและควรหลีกเลี่ยง ให้ใช้ + [NSLayoutConstraint deactivateConstraints:] แทน
eonist

@eonist คำตอบคือ 3 ปีที่แล้ว แทนที่จะวิจารณ์คุณควรแก้ไข
Alexander Volkov

5

รายละเอียด

  • Xcode 10.2.1 (10E1001), Swift 5

วิธีการแก้

import UIKit

extension UIView {

    func removeConstraints() { removeConstraints(constraints) }
    func deactivateAllConstraints() { NSLayoutConstraint.deactivate(getAllConstraints()) }
    func getAllSubviews() -> [UIView] { return UIView.getAllSubviews(view: self) }

    func getAllConstraints() -> [NSLayoutConstraint] {
        var subviewsConstraints = getAllSubviews().flatMap { $0.constraints }
        if let superview = self.superview {
            subviewsConstraints += superview.constraints.compactMap { (constraint) -> NSLayoutConstraint? in
                if let view = constraint.firstItem as? UIView, view == self { return constraint }
                return nil
            }
        }
        return subviewsConstraints + constraints
    }

    class func getAllSubviews(view: UIView) -> [UIView] {
        return view.subviews.flatMap { [$0] + getAllSubviews(view: $0) }
    }
}

การใช้งาน

print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)")
view.deactivateAllConstraints()

1
สวยดีนะ
eonist

2

โซลูชันที่รวดเร็ว:

extension UIView {
  func removeAllConstraints() {
    var view: UIView? = self
    while let currentView = view {
      currentView.removeConstraints(currentView.constraints.filter {
        return $0.firstItem as? UIView == self || $0.secondItem as? UIView == self
      })
      view = view?.superview
    }
  }
}

สิ่งสำคัญคือต้องผ่านผู้ปกครองทุกคนเนื่องจากบรรพบุรุษร่วมกันมีข้อ จำกัด ระหว่างสององค์ประกอบดังนั้นการล้าง superview ตามรายละเอียดในคำตอบนี้ไม่ดีพอและคุณอาจต้องเจอกับความประหลาดใจในภายหลัง


2

อ้างอิงจากคำตอบก่อนหน้านี้ (รวดเร็ว 4)

คุณสามารถใช้ instantConstraints เมื่อคุณไม่ต้องการรวบรวมข้อมูลลำดับชั้นทั้งหมด

extension UIView {
/**
 * Deactivates immediate constraints that target this view (self + superview)
 */
func deactivateImmediateConstraints(){
    NSLayoutConstraint.deactivate(self.immediateConstraints)
}
/**
 * Deactivates all constrains that target this view
 */
func deactiveAllConstraints(){
    NSLayoutConstraint.deactivate(self.allConstraints)
}
/**
 * Gets self.constraints + superview?.constraints for this particular view
 */
var immediateConstraints:[NSLayoutConstraint]{
    let constraints = self.superview?.constraints.filter{
        $0.firstItem as? UIView === self || $0.secondItem as? UIView === self
        } ?? []
    return self.constraints + constraints
}
/**
 * Crawls up superview hierarchy and gets all constraints that affect this view
 */
var allConstraints:[NSLayoutConstraint] {
    var view: UIView? = self
    var constraints:[NSLayoutConstraint] = []
    while let currentView = view {
        constraints += currentView.constraints.filter {
            return $0.firstItem as? UIView === self || $0.secondItem as? UIView === self
        }
        view = view?.superview
    }
    return constraints
}
}

2

ฉันใช้วิธีการต่อไปนี้เพื่อลบข้อ จำกัด ทั้งหมดออกจากมุมมอง:

ไฟล์. h:

+ (void)RemoveContraintsFromView:(UIView*)view 
    removeParentConstraints:(bool)parent 
    removeChildConstraints:(bool)child;

ไฟล์. m:

+ (void)RemoveContraintsFromView:(UIView *)view 
    removeParentConstraints:(bool)parent 
    removeChildConstraints:(bool)child
{
    if (parent) {
        // Remove constraints between view and its parent.
        UIView *superview = view.superview;
        [view removeFromSuperview];
        [superview addSubview:view];
    }

    if (child) {
        // Remove constraints between view and its children.
        [view removeConstraints:[view constraints]];
    }
}

คุณยังสามารถอ่านโพสต์นี้ในบล็อกของฉันเพื่อทำความเข้าใจวิธีการทำงานที่อยู่เบื้องหลังฝากระโปรง

หากคุณต้องการการควบคุมที่ละเอียดยิ่งขึ้นเราขอแนะนำให้เปลี่ยนไปใช้Masonryซึ่งเป็นคลาสเฟรมเวิร์กที่ทรงพลังที่คุณสามารถใช้ได้ทุกเมื่อที่คุณต้องการจัดการกับข้อ จำกัด อย่างถูกต้องโดยใช้โปรแกรม


2

วิธีที่ง่ายและมีประสิทธิภาพคือการลบมุมมองออกจาก superView และเพิ่มเป็นมุมมองย่อยอีกครั้ง สิ่งนี้ทำให้ข้อ จำกัด มุมมองย่อยทั้งหมดถูกลบออกโดยอัตโนมัติ


1

โดยมีวัตถุประสงค์ค

[self.superview.constraints enumerateObjectsUsingBlock:^(__kindof NSLayoutConstraint * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
        if (constraint.firstItem == self || constraint.secondItem == self) {
            [self.superview removeConstraint:constraint];
        }
    }];
    [self removeConstraints:self.constraints];
}

0

คุณสามารถใช้สิ่งนี้:

[viewA.superview.constraints enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
    NSLayoutConstraint *constraint = (NSLayoutConstraint *)obj;
    if (constraint.firstItem == viewA || constraint.secondItem == viewA) {
        [viewA.superview removeConstraint:constraint];
    }
}];

[viewA removeConstraints:viewA.constraints];

โดยทั่วไปสิ่งนี้จะแจกแจงข้อ จำกัด ทั้งหมดในมุมมองที่เหนือกว่าของ viewA และลบข้อ จำกัด ทั้งหมดที่เกี่ยวข้องกับ viewA

จากนั้นส่วนที่สองจะลบข้อ จำกัด ออกจาก viewA โดยใช้อาร์เรย์ของข้อ จำกัด ของ viewA


1
ปัญหาในการแก้ปัญหานี้คือจะลบเฉพาะข้อ จำกัด ที่เป็นของ superview เท่านั้น ไม่ได้ลบข้อ จำกัด ทั้งหมดที่มีผลต่อมุมมอง (เช่นคุณสามารถตั้งค่าข้อ จำกัด บน superview ของ superview ของ superview ได้อย่างง่ายดายซึ่งจะไม่ถูกลบออกโดยใช้วิธีนี้)
Senseful

ฉันเพิ่งอัปเดตคำถามเพื่อแสดงว่าเหตุใดโซลูชันนี้จึงใช้ไม่ได้ นี่เป็นวิธีที่ถูกต้องที่สุดเนื่องจากใกล้เคียงที่สุดกับโซลูชันกำลังเดรัจฉานที่ฉันกล่าวถึงข้างต้น อย่างไรก็ตามการแก้ปัญหานี้ไม่ถูกต้องจะลบและจะไม่ถูกต้องไม่ลบ[C7] [C3,C4]
Senseful

คุณสามารถออกแบบวิธีการรวบรวมข้อมูลตามลำดับชั้น แต่วิธีนี้ดีพอเพราะคุณควรเพิ่มข้อ จำกัด เหนือ superview ทันทีอยู่ดี IMO
eonist

0

(ณ วันที่ 31 กรกฎาคม 2560)

SWIFT 3

self.yourCustomView.removeFromSuperview()
self.yourCustomViewParentView.addSubview(self.yourCustomView)

วัตถุประสงค์ค

[self.yourCustomView removeFromSuperview];
[self.yourCustomViewParentView addSubview:self.yourCustomView];

นี่เป็นวิธีที่ง่ายที่สุดในการลบข้อ จำกัด ทั้งหมดที่มีอยู่ใน UIView อย่างรวดเร็ว อย่าลืมเพิ่ม UIView กลับด้วยข้อ จำกัด ใหม่หรือเฟรมใหม่หลังจากนั้น =)


0

ใช้ลำดับที่ใช้ซ้ำได้

ฉันตัดสินใจที่จะใช้วิธีนี้ด้วยวิธีที่ 'ใช้ซ้ำได้' มากขึ้น เนื่องจากการค้นหาข้อ จำกัด ทั้งหมดที่มีผลต่อมุมมองเป็นพื้นฐานสำหรับสิ่งที่กล่าวมาทั้งหมดฉันจึงตัดสินใจใช้ลำดับที่กำหนดเองซึ่งส่งคืนค่าทั้งหมดให้ฉันพร้อมกับการดูที่เป็นเจ้าของ

สิ่งแรกที่ต้องทำคือการกำหนดส่วนขยายบนArraysของNSLayoutConstraintผลตอบแทนที่ทุกองค์ประกอบที่มีผลต่อมุมมองที่เฉพาะเจาะจง

public extension Array where Element == NSLayoutConstraint {

    func affectingView(_ targetView:UIView) -> [NSLayoutConstraint] {

        return self.filter{

            if let firstView = $0.firstItem as? UIView,
                firstView == targetView {
                return true
            }

            if let secondView = $0.secondItem as? UIView,
                secondView == targetView {
                return true
            }

            return false
        }
    }
}

จากนั้นเราจะใช้ส่วนขยายนั้นในลำดับที่กำหนดเองซึ่งส่งคืนข้อ จำกัด ทั้งหมดที่มีผลต่อมุมมองนั้นพร้อมกับมุมมองที่เป็นเจ้าของจริง (ซึ่งอาจอยู่ที่ใดก็ได้ในลำดับชั้นของมุมมอง)

public struct AllConstraintsSequence : Sequence {

    public init(view:UIView){
        self.view = view
    }

    public let view:UIView

    public func makeIterator() -> Iterator {
        return Iterator(view:view)
    }

    public struct Iterator : IteratorProtocol {

        public typealias Element = (constraint:NSLayoutConstraint, owningView:UIView)

        init(view:UIView){
            targetView  = view
            currentView = view
            currentViewConstraintsAffectingTargetView = currentView.constraints.affectingView(targetView)
        }

        private let targetView  : UIView
        private var currentView : UIView
        private var currentViewConstraintsAffectingTargetView:[NSLayoutConstraint] = []
        private var nextConstraintIndex = 0

        mutating public func next() -> Element? {

            while(true){

                if nextConstraintIndex < currentViewConstraintsAffectingTargetView.count {
                    defer{nextConstraintIndex += 1}
                    return (currentViewConstraintsAffectingTargetView[nextConstraintIndex], currentView)
                }

                nextConstraintIndex = 0

                guard let superview = currentView.superview else { return nil }

                self.currentView = superview
                self.currentViewConstraintsAffectingTargetView = currentView.constraints.affectingView(targetView)
            }
        }
    }
}

ในที่สุดเราก็ประกาศส่วนขยายUIViewเพื่อเปิดเผยข้อ จำกัด ทั้งหมดที่มีผลต่อคุณสมบัติง่ายๆที่คุณสามารถเข้าถึงได้ด้วยไวยากรณ์ที่เรียบง่ายสำหรับแต่ละไวยากรณ์

extension UIView {

    var constraintsAffectingView:AllConstraintsSequence {
        return AllConstraintsSequence(view:self)
    }
}

ตอนนี้เราสามารถย้ำข้อ จำกัด ทั้งหมดที่มีผลต่อการดูและทำในสิ่งที่เราต้องการกับพวกเขาได้ ...

แสดงตัวระบุ ...

for (constraint, _) in someView.constraintsAffectingView{
    print(constraint.identifier ?? "No identifier")
}

ปิดใช้งาน ...

for (constraint, _) in someView.constraintsAffectingView{
    constraint.isActive = false
}

หรือลบออกทั้งหมด ...

for (constraint, owningView) in someView.constraintsAffectingView{
    owningView.removeConstraints([constraint])
}

สนุก!


0

รวดเร็ว

การติดตาม UIView Extension จะลบข้อ จำกัด Edge ทั้งหมดของมุมมอง:

extension UIView {
    func removeAllConstraints() {
        if let _superview = self.superview {
            self.removeFromSuperview()
            _superview.addSubview(self)
        }
    }
}

-1

นี่คือวิธีปิดการใช้งานข้อ จำกัด ทั้งหมดจากมุมมองเฉพาะ

 NSLayoutConstraint.deactivate(myView.constraints)

ปัญหาในการแก้ปัญหานี้คือจะลบเฉพาะข้อ จำกัด ที่มุมมองนี้เป็นเจ้าของ (เช่นความกว้างและความสูง) ไม่ได้ลบสิ่งต่างๆเช่นข้อ จำกัด ด้านบนและด้านบน
Senseful
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.