Yerleşim kısıtlamalarını ne zaman etkinleştirebilir / devre dışı bırakabilirim?


104

IB'de birden fazla kısıtlama seti kurdum ve bazı durumlara bağlı olarak bunlar arasında programlı olarak geçiş yapmak istiyorum. Orada bir var constraintsAIB yüklü olarak işaretlenmiş hepsi çıkış toplama ve constraintsBtüm çıkış toplama IB kaldırılır.

İki küme arasında programlı olarak şu şekilde geçiş yapabilirim:

NSLayoutConstraint.deactivateConstraints(constraintsA)
NSLayoutConstraint.activateConstraints(constraintsB)

Ama ... Bunu ne zaman yapacağımı bilemiyorum . Görünüşe göre bunu bir kez yapabilmeliyim viewDidLoadama işe yarayamıyorum. Aramayı view.updateConstraints()ve view.layoutSubviews()kısıtlamaları belirledikten sonra denedim , ancak boşuna.

Her şeydeki kısıtlamaları ayarlarsam viewDidLayoutSubviewsbeklendiği gibi çalıştığını buldum . Sanırım iki şeyi bilmek istiyorum ...

  1. Neden bu davranışı alıyorum?
  2. ViewDidLoad'dan kısıtlamaları etkinleştirmek / devre dışı bırakmak mümkün mü?

2
ViewWillLayoutSubviews'te deactivateConstraints ve activConstraints işlevlerinin çalıştığını mı kastediyorsunuz? Bunu denedim ve orada veya viewDidLoad'da çalışmadı. Bu, viewDidAppear içinde çalıştı; görünüm, yeni kısıtlamaların onu koyması gereken yerde ortaya çıktı, ancak manzaraya döndürürsem, görünüm IB'de belirlenen sınırlamalarla belirlenen konuma geri taşındı (ve portreye geri döndüğümde orada kaldı). Kısıtlamaların günlüğe kaydedilmesi, doğru olanları (yeni etkinleştirilenler) gösterdi. Bu bana bir böcek gibi görünüyor.
rdelmar

1
Evet, bunlar geçerliydi (viewDidAppear'da çalıştılar) ve super'i aramaya gerek yok çünkü viewWillLayoutSubviews'in varsayılan uygulaması yok (yine de super'i çağırarak denedim, ama bu fark yaratmadı).
rdelmar

1
@rdelmar Daha fazla test etme şansı buldum ... Aslında tarif ettiğiniz davranışa sahip olduğumu doğrulayabilirim ... ilk bakışta viewDidAppear'da çalışıyor, ancak daha sonra rotasyonla geri dönüyor.
tybro0103

3
Görünüşe göre kısıtlamaları bu amaçla IB'de kurulu değil olarak işaretleyemezsiniz. Bu bilgiyi burada buldum : stackoverflow.com/questions/27663249/… ve bu benim için sorunu çözdü.
Stefan

1
Bazılarını viewDidAppear'da etkinleştirmem / devre dışı bırakmam dışında, kısıtlamalarım soruyla aynı şekilde uygulandı. Bu işe yaradı, ancak öğelerin hızla konum değiştirdiğini görebiliyordunuz (küçük ama istenmeyen bir sorun). ViewWillAppear veya viewDidLoad'da değişiklik yapmak işe yaramadı. Ancak bu soruyu okuduktan sonra, bu değişikliği viewDidLayoutSubviews'te yapmayı denedim. Çalıştı ve konum değişikliği artık kullanıcı tarafından görülemiyor. (Ayrıca viewWillLayoutSubviews'te de çalıştı). Bu ipucu için teşekkürler!
barış tipi

Yanıtlar:


186

Ben etkinleştirmek ve devre dışı bırakmak NSLayoutConstraintsiçinde viewDidLoad, ve onunla herhangi bir sorun yok. Yani işe yarıyor. Uygulamanız ve benimki arasında kurulumda bir fark olmalı :-)

Sadece kurulumumu tarif edeceğim - belki size bir ipucu verebilir:

  1. @IBOutletsEtkinleştirmem / devre dışı bırakmam gereken tüm kısıtlamaları ayarlıyorum .
  2. İçinde ViewControllerkısıtlamaları zayıf olmayan sınıf özelliklerine kaydediyorum. Bunun nedeni, bir kısıtlamayı devre dışı bıraktıktan sonra onu yeniden etkinleştiremeyeceğimi bulmamdı - sıfırdı. Yani, devre dışı bırakıldığında silinmiş gibi görünüyor.
  3. Ben NSLayoutConstraint.deactivate/activatesizin yaptığınız gibi kullanmıyorum , onun yerine constraint.active = YES/ NOkullanıyorum.
  4. Kısıtlamaları belirledikten sonra ararım view.layoutIfNeeded().

132
"kısıtlamaları zayıf olmayan sınıf özelliklerine kaydedin" Bana çok zaman kazandırdınız, teşekkürler!
OpenUserX03

10
"Kısıtlamaları zayıf olmayan sınıf özelliklerine kaydediyorum": Bu beni tonlarca gönül yarasından kurtardı. Sıfır nesnesinde bir seçici çağırdığımı bilmiyordum. Teşekkürler!!
static0886

4
"Etkin olmayan" kısıtlamaların otomatik düzen tarafından göz ardı edilmediğini, kaldırıldığını unutmamak önemlidir. Kısıtlamaları etkinleştirmek / devre dışı bırakmak aslında onları ekler ve kaldırır. Daha önce belirlediğim kısıtlamaları ekledikten sonra, .active = falseonları etkin olarak ayarlayana kadar yok sayılmalarını bekledikten sonra, çakışan bir otomatik mizanpajda hata ayıklamak için biraz zaman harcadım .
lbarbosa

1
kısıtlamaları zayıf olmayan sınıf özelliklerine kaydedin, tamam bu çok zaman kazandırır, bu olmadan bazı karışık sonuçlar alıyordum. Teşekkürler dostum!
MegaManX

3
Apples doc şunu söylüyor: Bu kısıtlama tarafından yönetilen öğelerin en yakın ortak atası olan görünümde kısıtlama çağrılarının addConstraint ( :) ve removeConstraint ( :) 'ın etkinleştirilmesi veya devre dışı bırakılması . Doğrudan addConstraint ( :) veya removeConstraint ( :) çağırmak yerine bu özelliği kullanın . Bu nedenle, bir kısıtlama devre dışı bırakıldığında kaldırıldığı ve ardından IBOutlet güçlü olmadığı sürece, kalan kısıtlamaya yönelik güçlü referansların olmadığı görülmektedir. Bu nedenle kısıtlama silinir. IMHO bu neredeyse bir hata veya en azından çok beklenmedik bir davranıştır.
Olle Raab

52

Belki olabilir kontrol ettiğini @properties, yerini weakilestrong .

Bazen active = NOayarlandığı self.yourConstraint = niliçin self.yourConstrainttekrar kullanamayacaksın diye .


5
Swift Dil Kılavuzunda belirtildiği gibi , özellikler varsayılan olarak güçlüdür, bu yüzden sadece kaldırabilirsiniz weakve bu da halledecektir.
Jonathan Cabrera

30
override func viewDidLayoutSubviews() {
// do it here, after constraints have been materialized
}

1
Görüntü denetleyicim bir çocuk görüntüleme denetleyicisi olduğu için - bunu "didLayoutSubviews" içinde yapmak, işe yarayan tek yol gibi görünüyor !! FYI.
TalL

Tek geçerli cevap bu
Yunus Eren Güzel

@TalL Çocuk görüntüleme denetleyicisinin kendisindeki kısıtlamaları mı yoksa alt görünümlerini mi kastettiniz?
Stefan

bu en iyisi
ACAkgul

14

Yaşadığınız sorunun, AFTER viewDidLoad()çağrılana kadar görüşlerine eklenmeyen kısıtlamalardan kaynaklandığına inanıyorum . Bir dizi seçeneğiniz var:

Y) Yerleşim kısıtlamalarınızı bir IBOutlet'e bağlayabilir ve bu referanslarla bunlara kodunuzdan erişebilirsiniz. Çıkışlar başlamadan önce bağlandığından viewDidLoad(), kısıtlamalar erişilebilir olmalı ve orada onları etkinleştirmeye ve devre dışı bırakmaya devam edebilirsiniz.

B) UIView constraints()işlevini çeşitli kısıtlamalara erişmek için kullanmak istiyorsanız viewDidLayoutSubviews(), başlama işlemini beklemeniz ve orada yapmanız gerekir , çünkü bu, bir uçtan bir görüntüleme denetleyicisi oluşturduktan sonra herhangi bir yüklü kısıtlamaya sahip olacağı ilk noktadır. layoutIfNeeded()İşin bittiğinde aramayı unutma . Bunun dezavantajı, uygulanacak herhangi bir değişiklik olması durumunda düzen geçişinin iki kez gerçekleştirilmesi ve sonsuz bir döngünün tetiklenme olasılığı olmadığından emin olmanız gerekir.

Hızlı bir uyarı: devre dışı bırakılan kısıtlamalar yöntem tarafından döndürülmez constraints() ! Bu, bir kısıtlamayı daha sonra tekrar açmak amacıyla devre dışı bırakırsanız, ona bir referans tutmanız gerekeceği anlamına gelir.

C) Film şeridi yaklaşımını unutabilir ve bunun yerine kısıtlamalarınızı manuel olarak ekleyebilirsiniz. Bunu içinde yaptığınız viewDidLoad()için, amacın, düzeni anında değiştirmek yerine nesnenin tüm ömrü boyunca yalnızca bir kez yapmak olduğunu varsayıyorum, bu nedenle bu kabul edilebilir bir yöntem olmalıdır.


10

Ayrıca priorityözelliği, bunları "etkinleştirmek" ve "devre dışı bırakmak" için ayarlayabilirsiniz (örneğin, etkinleştirmek için 750 değer ve devre dışı bırakmak için 250 değer). Bazı nedenlerden dolayı activeBOOL'u değiştirmenin kullanıcı arayüzüm üzerinde herhangi bir etkisi olmadı. Gerek yok layoutIfNeeded, viewDidLoad'da veya bundan sonra herhangi bir zamanda ayarlanıp değiştirilebilir.


Çok güzel bir öneri. Kısıtlama önceliğini değiştirmek viewWillTransition(to:, with:)veya içinde çalışır viewWillLayoutSubviews()ve tüm alternatif kısıtlamalarınızı bir film şeridinde "kurulu" olarak tutabilirsiniz. Kısıtlama önceliği gerekli olmayandan gerekli olana değiştirilemez, bu nedenle aşağıdaki değerleri kullanın 1000. Öte yandan, kısıtlamaları etkinleştirmek (eklemek) ve devre dışı bırakmak (kaldırmak) yalnızca içinde çalışır viewDidLayoutSubviews()ve strong @IBOutletreferansların NSLayoutConstraint-s olarak tutulmasını gerektirir .
Gary

"Bazı nedenlerden dolayı aktif BOOL'u değiştirmenin kullanıcı arayüzüm üzerinde herhangi bir etkisi olmadı". Dayanarak burada . Çalışma zamanı sırasında 1000 öncelikli bir kısıtlamayı değiştiremeyeceğinizi düşünüyorum . Devre dışı bırakmak istiyorsanız, başlangıç ​​önceliğini 999 veya daha düşük bir değere ayarlamalısınız ....
Honey

Bu ifadeye katılmıyorum çünkü sorunların giderilmesi zor olabilir ve soruyu cevaplamaz. Önceliğin 250 olarak ayarlanması kısıtlamayı "devre dışı bırakmaz", yine de etkisi olur ve düzeni etkiler. Çoğu durumda kısıtlamayı "devre dışı bırakıyor" gibi görünebilir, ancak kesinlikle her durumda değil. (özellikle bu soruya cevap bulmama neden olan durum değil)
Tumata

Bu, "Önceliğin kurulu bir kısıtlamada gerekenden gerekenden değiştirilmesine (veya tersi) desteklenmediği gibi çökmelere neden olabilir. Öncelik 250'yi geçtiniz ve mevcut öncelik 1000'di."
Karthick Ramesh

8

Kullanılmayan kısıtlamaları devre dışı bırakmak için uygun zaman:

-(void)viewWillLayoutSubviews{
    [super viewWillLayoutSubviews];

    self.myLittleConstraint.active = NO;
}

Bunun viewWillLayoutSubviewsbirden çok kez çağrılabileceğini unutmayın , bu yüzden burada ağır hesaplamalar yok, tamam mı?

Not: Bazı kısıtlamaları daha sonra yeniden etkinleştirmek isterseniz, her zaman strongbunlara referansı saklayın .


2
Benim için tek güvenilir yol, kısıtlamaları ayarlamaktır viewDidLayoutSubviews(). viewWillLayoutSubviews()Benim durumumda kısıtlamaları ayarlamak işe yaramıyor.
petrsyn

6

Bir görünüm oluşturulurken aşağıdaki yaşam döngüsü yöntemleri sırayla çağrılır:

  1. loadView
  2. viewDidLoad
  3. viewWillAppear
  4. viewWillLayoutSubviews
  5. viewDidLayoutSubviews
  6. viewDidAppear

Şimdi sorularınıza.

  1. Neden bu davranışı alıyorum?

Cevap: Çünkü viewDidLoadgörünümdeki görünümler üzerindeki kısıtlamaları ayarlamaya çalıştığınızda sınırları yoktur, dolayısıyla kısıtlamalar ayarlanamaz. Ancak bundan sonra viewDidLayoutSubviewsgörünümün sınırları kesinleşir.

  1. ViewDidLoad'dan kısıtlamaları etkinleştirmek / devre dışı bırakmak mümkün mü?

Cevap: Hayır. Nedeni yukarıda açıklanmıştır.


ViewController yaşam döngüsünün açıklamasında, görünümün ilk olarak nasıl yüklendiğinden ve ardından viewDidLoad'un çağrıldığından bahsettiniz. Yine de görüşün time viewDidLoad denildiğinde yaratılmadığını söylediniz, bu açıkça bir çelişkidir. Ayrıca, kendiniz için test edebilir ve görünümün, görünüme alt görünümler ekleyebildiğiniz için viewDidLoad çağrıldığı zaman tarafından oluşturulduğunu görebilirsiniz.
ABakerSmith

viewDidLoad, görünümler oluşturulduğu ve yüklendiğinden iyi olmalıdır ... Gerçekte, kısıtlamaları etkinleştirdiğiniz yer, esas olarak performansa bağlıdır. Tahminimce asıl sorun, kısıtlamaların nerede etkinleştirildiği ile ilgisizdi. stackoverflow.com/questions/19387998/…
Gabe

@ABakerSmith cevabımı daha net olacak şekilde düzenledim.
Sumeet

1

- (void)updateConstraints(Hedef c) ' nin geçersiz kılmasında normal başına kısıtlamaları ayarladığınız sürece, strongkullanılan başlangıç ​​için aktif ve aktif olmayan kısıtlamalar için bir referans buldum . Ve görünüm döngüsünün başka bir yerinde ihtiyacınız olanı devre dışı bırakın ve / veya etkinleştirin, ardından arayın layoutIfNeeded, sorun yaşamazsınız.

Esas updateConstraintsolan, updateConstraintilk başlatmanızdan ve düzeninizden sonra s'yi çağırdığınız sürece, sürekli olarak geçersiz kılmayı tekrar kullanmak ve kısıtlamaların etkinleştirmelerini ayırmak değildir . Bundan sonra görüş döngüsünün neresinde olduğu önemli görünüyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.