Saya memiliki UIView yang ditempatkan di layar melalui beberapa kendala. Beberapa batasan dimiliki oleh superview, yang lainnya dimiliki oleh leluhur lain (misalnya, mungkin properti tampilan dari UIViewController).
Saya ingin menghapus semua batasan lama ini, dan menempatkannya di tempat baru menggunakan batasan baru.
Bagaimana saya bisa melakukan ini tanpa membuat IBOutlet untuk setiap batasan dan harus mengingat tampilan mana yang memiliki batasan tersebut?
Untuk menguraikan, pendekatan naif akan membuat sekelompok IBOutlet untuk setiap batasan, dan kemudian akan melibatkan kode panggilan seperti:
[viewA removeConstraint:self.myViewsLeftConstraint];
[viewB removeConstraint:self.myViewsTopConstraint];
[viewB removeConstraint:self.myViewsBottomConstraint];
[self.view removeConstraint:self.myViewsRightConstraint];
Masalah dengan kode ini adalah bahwa bahkan dalam kasus yang paling sederhana, saya perlu membuat 2 IBOutlets. Untuk tata letak yang kompleks, ini dapat dengan mudah mencapai 4 atau 8 IBOutlet yang diperlukan. Selain itu, saya perlu memastikan bahwa panggilan saya untuk menghapus batasan dipanggil pada tampilan yang tepat. Misalnya, bayangkan yang myViewsLeftConstraint
dimiliki olehviewA
. Jika saya tidak sengaja menelepon [self.view removeConstraint:self.myViewsLeftConstraint]
, tidak akan terjadi apa-apa.
Catatan: Metode constraintsAffectingLayoutForAxis terlihat menjanjikan, tetapi dimaksudkan untuk tujuan debug saja.
Update: Banyak jawaban saya menerima kesepakatan dengan self.constraints
, self.superview.constraints
atau beberapa varian dari mereka. Solusi ini tidak akan berfungsi karena metode tersebut hanya mengembalikan batasan yang dimiliki oleh tampilan, bukan yang memengaruhi tampilan.
Untuk memperjelas masalah dengan solusi ini, pertimbangkan hierarki tampilan ini:
- Kakek
- Ayah
- Saya
- Putra
- Putri
- Saudara
- Saya
- Paman
- Ayah
Sekarang bayangkan kita membuat batasan berikut, dan selalu melampirkannya ke leluhur terdekat yang terdekat:
- C0: Aku: sama dengan Anak (milik Aku)
- C1: Me: width = 100 (milik Me)
- C2: Aku: sama tingginya dengan Kakak (milik Ayah)
- C3: Saya: sama dengan Paman (milik Kakek)
- C4: Saya: kiri sama dengan Kakek (milik Kakek)
- C5: Kakak: kiri sama dengan Ayah (milik Ayah)
- C6: Paman: kiri sama dengan Kakek (milik Kakek)
- C7: Putra: kiri sama dengan Putri (milik Saya)
Sekarang bayangkan kita ingin menghapus semua kendala yang mempengaruhi Me
. Solusi yang tepat harus dihapus [C0,C1,C2,C3,C4]
dan tidak ada yang lain.
Jika Aku menggunakan self.constraints
(di mana diri adalah Aku), aku akan mendapatkannya [C0,C1,C7]
, karena hanya itu kendala yang dimiliki oleh-Ku. Jelas itu tidak akan cukup untuk menghapus ini karena hilang [C2,C3,C4]
. Selain itu, menghapus secara C7
tidak perlu.
Jika saya pakai self.superview.constraints
(di mana diri adalah Aku), saya akan dapatkan [C2,C5]
, karena itu adalah kendala yang dimiliki oleh Bapa. Jelas kami tidak dapat menghapus semua ini karena C5
sama sekali tidak terkait dengan Me
.
Jika saya menggunakan grandfather.constraints
, saya akan mendapatkan [C3,C4,C6]
. Sekali lagi, kami tidak dapat menghapus semua ini karena C6
harus tetap utuh.
Pendekatan kekerasan adalah untuk loop atas setiap nenek moyang pandangan (termasuk dirinya sendiri), dan melihat apakah firstItem
atau secondItem
adalah pandangan sendiri; jika demikian, hilangkan batasan itu. Ini akan mengarah pada solusi yang benar, kembali [C0,C1,C2,C3,C4]
, dan hanya kendala itu.
Namun, saya berharap ada solusi yang lebih elegan daripada harus menelusuri seluruh daftar leluhur.
sumber
Jawaban:
Pendekatan ini berhasil untuk saya:
@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
Setelah selesai mengeksekusi tampilan Anda tetap di tempatnya karena itu menciptakan batasan autoresisasi. Jika saya tidak melakukan ini, pemandangan biasanya menghilang. Selain itu, ini tidak hanya menghapus batasan dari superview tetapi juga melintasi seluruhnya karena mungkin ada batasan yang mempengaruhinya dalam tampilan leluhur.
Versi 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 } }
sumber
C7
. Namun, ini akan mudah diperbaiki jika Anda menghapus[self removeConstraints:self.constraints];
dan mengubahnyaUIView *superview = self.superview;
keUIView *superview = self;
.Satu-satunya solusi yang saya temukan sejauh ini adalah menghapus tampilan dari superview-nya:
Sepertinya ini menghapus semua batasan yang memengaruhi tata letaknya dan siap ditambahkan ke superview dan memasang batasan baru. Namun, ini juga akan menghapus subview dari hierarki dengan benar, dan menghapusnya dengan
[C7]
tidak benar.sumber
Anda dapat menghapus semua batasan dalam satu tampilan dengan melakukan ini:
self.removeConstraints(self.constraints)
EDIT: Untuk menghapus batasan semua subview, gunakan ekstensi berikut di Swift:
extension UIView { func clearConstraints() { for subview in self.subviews { subview.clearConstraints() } self.removeConstraints(self.constraints) } }
sumber
self.constraints
hanya mengembalikan batasan yangself
dimiliki, tidak semua batasan yang mempengaruhiself
.[C6]
, dan salah tidak menghapus[C0,C1,C2]
.Ada dua cara bagaimana mencapai itu menurut Dokumentasi Pengembang Apple
1.
NSLayoutConstraint.deactivateConstraints
// Declaration class func deactivate(_ constraints: [NSLayoutConstraint]) // Usage NSLayoutConstraint.deactivate(yourView.constraints)
2.
UIView.removeConstraints
(Tidak digunakan lagi untuk> = iOS 8.0)// Declaration func removeConstraints(_ constraints: [NSLayoutConstraint])` // Usage yourView.removeConstraints(yourView.constraints)
Tips
Menggunakan
Storyboard
s atauXIB
s bisa sangat merepotkan dalam mengonfigurasi batasan seperti yang disebutkan pada skenario Anda, Anda harus membuat IBOutlet untuk setiap yang ingin Anda hapus. Meski begitu, sebagian besar waktuInterface Builder
menciptakan lebih banyak masalah daripada menyelesaikannya.Oleh karena itu, ketika memiliki konten yang sangat dinamis dan keadaan tampilan yang berbeda, saya akan menyarankan:
Kode Sederhana
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() }
Referensi
sumber
Di 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) } }
sumber
if c.firstItem === self
daripada mentransmisikanas? UIView
dan memeriksa kesetaraan dengan==
Detail
Larutan
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) } } }
Pemakaian
print("constraints: \(view.getAllConstraints().count), subviews: \(view.getAllSubviews().count)") view.deactivateAllConstraints()
sumber
Solusi Cepat:
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 } } }
Penting untuk membahas semua orang tua, karena batasan antara dua elemen dipegang oleh nenek moyang yang sama, jadi menghapus superview seperti yang dijelaskan dalam jawaban ini tidak cukup baik, dan Anda mungkin akan mendapatkan kejutan buruk di kemudian hari.
sumber
Berdasarkan jawaban sebelumnya (swift 4)
Anda dapat menggunakan segeraConstraints jika tidak ingin merayapi seluruh hierarki.
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 } }
sumber
Saya menggunakan metode berikut untuk menghapus semua kendala dari tampilan:
file .h:
+ (void)RemoveContraintsFromView:(UIView*)view removeParentConstraints:(bool)parent removeChildConstraints:(bool)child;
file .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]]; } }
Anda juga dapat membaca posting ini di blog saya untuk lebih memahami cara kerjanya di balik terpal.
Jika Anda memerlukan kontrol yang lebih terperinci, saya sangat menyarankan untuk beralih ke Masonry , kelas kerangka kerja yang kuat yang dapat Anda gunakan kapan pun Anda perlu menangani batasan secara terprogram dengan benar.
sumber
Pendekatan yang lebih mudah dan efisien adalah menghapus tampilan dari superView dan menambahkan kembali sebagai subview lagi. ini menyebabkan semua batasan subview dihapus secara otomatis.😉
sumber
Dengan objektifC
[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]; }
sumber
Anda bisa menggunakan sesuatu seperti ini:
[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];
Pada dasarnya, ini menyebutkan semua batasan pada superview viewA dan menghapus semua batasan yang terkait dengan viewA.
Kemudian, bagian kedua menghilangkan batasan dari viewA menggunakan larik batasan viewA.
sumber
[C7]
dan salah tidak akan menghapus[C3,C4]
.(Per 31 Juli 2017)
SWIFT 3
self.yourCustomView.removeFromSuperview() self.yourCustomViewParentView.addSubview(self.yourCustomView)
Tujuan C
[self.yourCustomView removeFromSuperview]; [self.yourCustomViewParentView addSubview:self.yourCustomView];
Ini adalah cara termudah untuk dengan cepat menghapus semua kendala yang ada di UIView. Pastikan untuk menambahkan UIView kembali dengan batasan baru atau bingkai baru setelahnya =)
sumber
Menggunakan Urutan yang Dapat Digunakan Kembali
Saya memutuskan untuk melakukan pendekatan ini dengan cara yang lebih 'dapat digunakan kembali'. Karena menemukan semua batasan yang memengaruhi tampilan adalah dasar untuk semua hal di atas, saya memutuskan untuk menerapkan urutan khusus yang mengembalikan semuanya untuk saya, bersama dengan tampilan yang dimiliki.
Hal pertama yang harus dilakukan adalah menentukan perpanjangan
Arrays
dariNSLayoutConstraint
yang kembali semua elemen yang mempengaruhi pandangan tertentu.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 } } }
Kami kemudian menggunakan ekstensi itu dalam urutan kustom yang mengembalikan semua batasan yang memengaruhi tampilan tersebut, bersama dengan tampilan yang benar-benar memilikinya (yang bisa berada di mana saja di atas hierarki tampilan)
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) } } } }
Akhirnya kami mendeklarasikan ekstensi on
UIView
untuk mengekspos semua batasan yang mempengaruhinya dalam properti sederhana yang dapat Anda akses dengan sintaks sederhana untuk setiap.extension UIView { var constraintsAffectingView:AllConstraintsSequence { return AllConstraintsSequence(view:self) } }
Sekarang kita dapat mengulangi semua batasan yang memengaruhi tampilan dan melakukan apa yang kita inginkan dengannya ...
Cantumkan pengenal mereka ...
for (constraint, _) in someView.constraintsAffectingView{ print(constraint.identifier ?? "No identifier") }
Nonaktifkan mereka ...
for (constraint, _) in someView.constraintsAffectingView{ constraint.isActive = false }
Atau hapus seluruhnya ...
for (constraint, owningView) in someView.constraintsAffectingView{ owningView.removeConstraints([constraint]) }
Nikmati!
sumber
Cepat
Mengikuti Ekstensi UIView akan menghapus semua batasan Edge dari sebuah tampilan:
extension UIView { func removeAllConstraints() { if let _superview = self.superview { self.removeFromSuperview() _superview.addSubview(self) } } }
sumber
Ini adalah cara untuk menonaktifkan semua kendala dari tampilan tertentu
NSLayoutConstraint.deactivate(myView.constraints)
sumber