UIStackView "Sıkıştırılmış" gizli görünümlerde "aynı anda kısıtlamaları karşılayamıyor"


97

UIStackView "satırlarım" sıkıştırıldığında AutoLayoutuyarılar veriyorlar . Ancak, iyi görünüyorlar ve bu tür kayıtların dışında başka hiçbir şey yanlış değil:

Aynı anda kısıtlamaları karşılayamıyor. Muhtemelen aşağıdaki listedeki kısıtlamalardan en az biri istemediğinizdir. Şunu deneyin: (1) her bir kısıtlamaya bakın ve hangisini beklemediğinizi anlamaya çalışın; (2) istenmeyen kısıtlamaları veya kısıtlamaları ekleyen kodu bulun ve düzeltin. (Not: NSAutoresizingMaskLayoutConstraintsAnlamadığınızı görüyorsanız , UIViewmülkün belgelerine bakın translatesAutoresizingMaskIntoConstraints) (

Öyleyse, bunu nasıl düzelteceğimi henüz bilmiyorum, ancak sinir bozucu olmanın yanı sıra hiçbir şeyi kırmıyor gibi görünüyor.

Bunu nasıl çözeceğini bilen var mı? İlginç bir şekilde, düzen kısıtlamaları sık sık 'UISV gizleme' ile etiketlenir ve bu da belki de alt görünümler için yükseklik minimumlarını veya bu örnekteki bir şeyi göz ardı etmesi gerektiğini belirtir.


1
Bu, iOS11'de düzeltilmiş gibi görünüyor, burada herhangi bir uyarı almıyor
tuzakçı

Yanıtlar:


206

Bu sorunu yaşarsınız, çünkü bir alt görünümü içeriden gizliye ayarlarken UIStackView, onu canlandırmak için önce yüksekliğini sıfırla sınırlar.

Şu hatayı alıyordum:

2015-10-01 11:45:13.732 <redacted>[64455:6368084] Unable to simultaneously satisfy constraints.
    Probably at least one of the constraints in the following list is one you don't want. Try this: (1) look at each constraint and try to figure out which you don't expect; (2) find the code that added the unwanted constraint or constraints and fix it. (Note: If you're seeing NSAutoresizingMaskLayoutConstraints that you don't understand, refer to the documentation for the UIView property translatesAutoresizingMaskIntoConstraints) 
(
    "<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5be508d0 V:|-(8)-[UISegmentedControl:0x7f7f5bec4180]   (Names: '|':UIView:0x7f7f5be69d30 )>",
    "<NSLayoutConstraint:0x7f7f5bdfbda0 'UISV-hiding' V:[UIView:0x7f7f5be69d30(0)]>"
)

Will attempt to recover by breaking constraint 
<NSLayoutConstraint:0x7f7f5be18c80 V:[UISegmentedControl:0x7f7f5bec4180]-(8)-|   (Names: '|':UIView:0x7f7f5be69d30 )>

Make a symbolic breakpoint at UIViewAlertForUnsatisfiableConstraints to catch this in the debugger.
The methods in the UIConstraintBasedLayoutDebugging category on UIView listed in <UIKit/UIView.h> may also be helpful.

Yapmaya çalıştığım şey, her bir kenara 8 puntoluk bir ek içeren UIViewbenim içine UIStackViewbir UISegmentedControlyerleştirmekti.

Bunu gizli olarak ayarladığımda, konteyner görünümünü sıfır yükseklikle sınırlamaya çalışırdı, ancak yukarıdan aşağıya bir dizi kısıtlamam olduğu için bir çakışma oldu.

Sorunu çözmek için, 8pt üst değerimi en alt sınırlama önceliğini 1000'den 999'a değiştirdim, böylece gerekirse UISV-hidingkısıtlama öncelik alabilir.


Bunlarda sadece bir yükseklik veya genişlik kısıtlamanız varsa ve önceliklerini azaltırsanız, işe yaramayacağı belirtilmelidir. Yüksekliği / genişliği kaldırmanız ve en üstteki sondaki altları eklemeniz, ardından önceliklerini daha düşük olarak ayarlamanız gerekir ve işe yarıyor
bolnad

4
Öncelikleri değiştirmek benim için de işe yaradı. Ayrıca, kullanılmayan Boyut Sınıflarından yanlışlıkla kopyalanan fazlalık (soluk) Kısıtlamaların kaldırılması. ÖNEMLİ İPUCU: Bu sorunlarda daha kolay hata ayıklamak için, her Kısıtlama için bir IDENTIFER dizesi ayarlayın. Daha sonra hata ayıklama mesajında ​​hangi Kısıtlamanın yaramaz olduğunu görebilirsiniz.
Womble

3
Benim durumumda, sadece yüksekliğe öncelik vermem gerekiyor ve işe yarıyor.
pixelfreak

Bu IDENTIFIER ipucu harika! Her zaman hata ayıklama mesaj adlarındaki kısıtlamaları nasıl vereceğimi merak etmişimdir, her zaman kısıtlamanın kendisinden ziyade görünüme bir şeyler eklemek istedim. Teşekkürler @Womble!
Ryan

Teşekkürler! 1000'den 999'a kadar olan öncelik hile yaptı. Xcode: Sürüm 8.3.3 (8E3004b)
Michael Garito

54

Çözmesi kolay olmayan benzer bir sorun yaşıyordum. Benim durumumda, yığın görünümüne gömülü bir yığın görünümüm vardı. Dahili UIStackView iki etikete ve sıfır olmayan bir aralığa sahipti.

AddArrangedSubview () öğesini çağırdığınızda, aşağıdakine benzer kısıtlamaları otomatik olarak oluşturur:

V:|[innerStackView]|              | = outerStackView

  V:|[label1]-(2)-[label2]|       | = innerStackView

Şimdi, innerStackView'ı gizlemeye çalıştığınızda, belirsiz bir kısıtlama uyarısı alırsınız.

Neden anlamak için, bu yüzden ilk görelim vermez zaman ne innerStackView.spacingeşittir 0. Aradığınızda innerStackView.hidden = true, @liamnichols doğruydu ... outerStackViewsihirli bu çağrı yolunu kesmek ve bir yaratacak 0yükseklik UISV gizleme önceliği 1000 ile kısıtlamayla (gerekli). Muhtemelen bu, gizleme kodunuzun bir UIView.animationWithDuration()blok içinde çağrılması durumunda yığın görünümündeki öğelerin görünüm dışında canlandırılmasına izin vermektir . Ne yazık ki, bu kısıtlamanın eklenmesini engellemenin bir yolu yok gibi görünüyor. Bununla birlikte, aşağıdakiler meydana geldiği için "Sınırlamaları aynı anda karşılayamıyorum" (USSC) uyarısı almazsınız:

  1. label1'in yüksekliği 0'a ayarlandı
  2. iki etiket arasındaki boşluk zaten 0 olarak tanımlanmıştı
  3. label2'nin yüksekliği 0'a ayarlandı
  4. innerStackView yüksekliği 0 olarak ayarlandı

Bu 4 kısıtlamanın yerine getirilebileceği açıktır. Yığın görünümü her şeyi basitçe 0 yüksekliğinde bir piksele dönüştürür.

Belirlediğimiz Şimdi, eğer arabası örneğe geri gidiş spacingiçin 2, şimdi bu kısıtlamaları vardır:

  1. label1'in yüksekliği 0'a ayarlandı
  2. iki etiket arasındaki boşluk, yığın görünümü tarafından 1000 önceliğe sahip 2 piksel yüksekliğinde otomatik olarak oluşturuldu.
  3. label2'nin yüksekliği 0'a ayarlandı
  4. innerStackView yüksekliği 0 olarak ayarlandı

Yığın görünümü hem 0 piksel yüksekliğinde hem de içeriği 2 piksel yüksekliğinde olamaz. Kısıtlamalar tatmin edilemez.

Not: Bu davranışı daha basit bir örnekle görebilirsiniz. Düzenlenmiş bir alt görünüm olarak yığın görünümüne bir UIView eklemeniz yeterlidir. Ardından, bu UIView için 1000 önceliğe sahip bir yükseklik kısıtlaması ayarlayın. Şimdi sakla demeyi dene.

Not: Nedeni ne olursa olsun, bu yalnızca yığın görünümüm bir UICollectionViewCell veya UITableViewCell alt görünümü olduğunda oldu. Ancak, innerStackView.systemLayoutSizeFittingSize(UILayoutFittingCompressedSize)iç yığın görünümünü gizledikten sonra bir sonraki çalıştırma döngüsünü çağırarak bu davranışı bir hücrenin dışında da yeniden oluşturabilirsiniz .

Not: Bir UIView.performWithoutAnimations içindeki kodu çalıştırmayı deneseniz bile, yığın görünümü yine de USSC uyarısına neden olacak bir 0 yükseklik kısıtlaması ekleyecektir.


Bu sorunun en az 3 çözümü var:

  1. Yığın görünümünde herhangi bir öğeyi gizlemeden önce, bunun bir yığın görünümü olup olmadığını kontrol edin ve öyleyse spacing0 olarak değiştirin. Bu can sıkıcı çünkü içeriği her gösterdiğinizde işlemi tersine çevirmeniz (ve orijinal aralığı hatırlamanız) gerekiyor.
  2. Öğeleri yığın görünümünde gizlemek yerine arayın removeFromSuperview. İşlemi tersine çevirdiğinizde , çıkarılan öğeyi nereye yerleştireceğinizi hatırlamanız gerektiğinden, bu daha da can sıkıcı bir durumdur . Yalnızca removeArrangedSubview'u çağırıp sonra gizleyerek optimizasyon yapabilirsiniz, ancak yine de yapılması gereken çok sayıda defter tutma vardır.
  3. Bir UIView'de iç içe geçmiş yığın görünümlerini (sıfır olmayan spacing) sarın . En az bir kısıtlamayı gerekli olmayan öncelik olarak belirtin (999 veya altı). Herhangi bir muhasebe işlemi yapmanız gerekmediği için bu en iyi çözümdür. Örneğimde, yığın görünümü ile sarmalayıcı görünümü arasında 1000'de üst, ön ve son kısıtlamalar oluşturdum, ardından yığın görünümünün altından sarıcı görünümüne kadar 999 sınırlama oluşturdum. Bu şekilde, dış yığın görünümü sıfır yükseklik kısıtlaması oluşturduğunda, 999 kısıtlaması bozulur ve USSC uyarısını görmezsiniz. (Not: Bu, bir UICollectionViewCell alt sınıfının contentView.translatesAutoResizingMaskToConstraints değerinin ayarlanması gerekenfalse çözümüne benzer )

Özet olarak, bu davranışa sahip olmanızın nedenleri şunlardır:

  1. Bir yığın görünümüne yönetilen alt görünümler eklediğinizde, Apple sizin için otomatik olarak 1000 öncelikli kısıtlama oluşturur.
  2. Bir yığın görünümünün alt görünümünü gizlediğinizde, Apple sizin için otomatik olarak bir 0 yükseklik sınırlaması oluşturur.

Apple ya (1) kısıtlamaların önceliğini (özellikle ayırıcıların) belirlemenize izin verseydi ya da (2) otomatik UISV gizleme kısıtlamasından vazgeçmenize izin verseydi , bu sorun kolayca çözülebilirdi.


6
Süper kapsamlı ve faydalı açıklama için teşekkürler. Ancak bu kesinlikle Apple tarafında yığın görünümlerinde bir hata gibi görünüyor. Temelde "gizleme" özelliği, "boşluk" özelliği ile uyumsuzdur. O zamandan beri bunu çözdüklerine dair herhangi bir fikir veya fazladan kapsayıcı görünümlerle dolaşmayı önlemek için bazı özellikler eklediler mi? (Yine, potansiyel çözümlerin büyük dökümü ve # 3'ün zarafeti üzerinde hemfikir olun)
Marchy

1
Her UIStackViewçocuğun tek başına mı yoksa sadece gizlemek / göstermek istediğiniz çocuğa mı UIStackViewsarılması gerekiyor UIView?
Adrian

1
Bu, Apple açısından gerçekten kötü bir gözetim gibi geliyor. Özellikle kullanımı çevreleyen uyarılar ve hatalar UIStackViewgenellikle şifreli ve anlaşılması zor olduğu için.
bompf

Bu bir cankurtarandır. Bir UITableViewCell'e eklenen UIStackViews, hücre her yeniden kullanıldığında AutoLayout hata günlüğünde spam gönderilmesine neden olan bir sorun yaşıyordum. StackView'ün alt bağlantı kısıtlaması önceliği düşük olarak ayarlanmış bir UIView'de gömülmesi sorunu çözdü. Görünüm hata ayıklayıcısının stackView öğelerini belirsiz yüksekliklere sahip olarak göstermesine neden olur, ancak uygulamada hata günlüğü spam'i olmadan düzgün şekilde gösterilir. TEŞEKKÜR EDERİM.
Womble

6

Çoğu zaman bu hata, çakışmaları ortadan kaldırmak için kısıtlama önceliğini düşürerek çözülebilir.


Ne demek istiyorsun? Kısıtlamalar yığılmış görünümler içinde görecelidir ....
Ben Guild

Üzgünüm, sorunuzu doğru anlamıyorum, ingilizcem öyle, ama bence kısıtlamalar ilişkili olduğu görüş cadı ile ilgilidir, eğer görüş gizlenirse kısıtlamalar etkisiz hale gelir. Şüphen bu mu emin değilim, ama umarım yardım edebilirim.
Luciano Almeida

Bir şeyler ezildiği veya gösterilme / gizlenme sürecinden geliyor gibi görünüyor. Bu durumda kısmen görünür hale gelir. - Belki de minimum dikey sabitleri gerçekten geçip ortadan kaldırmak gerekiyor çünkü 0 yüksekliğe kadar ezilebilir?
Ben Guild

2

Bir görünümü gizli olarak ayarladığınızda, görünüm UIStackviewonu uzaklaştırmaya çalışır. Bu etkiyi istiyorsanız, kısıtlamalar için doğru önceliği ayarlamanız gerekir, böylece çatışmasınlar (yukarıda birçoklarının önerdiği gibi).

Bununla birlikte, eğer animasyonu umursamıyorsanız (belki de onu ViewDidLoad'da saklıyorsunuz), o zaman removeFromSuperviewaynı etkiye sahip olacak, ancak kısıtlamalarla ilgili herhangi bir sorun olmadan, bunlar görünümle birlikte kaldırılacaklarından basitleştirebilirsiniz .


1

@ Senseful'ın cevabına dayanarak, bir görünümde bir yığın görünümünü sarmak ve önerdiği kısıtlamaları uygulamak için bir UIStackView uzantısı:

/// wraps in a `UIView` to prevent autolayout warnings when a stack view with spacing is placed inside another stack view whose height might be zero (usually due to `hidden` being `true`).
/// See http://stackoverflow.com/questions/32428210
func wrapped() -> UIView {
    let wrapper = UIView()
    translatesAutoresizingMaskIntoConstraints = false
    wrapper.addSubview(self)

    for attribute in [NSLayoutAttribute.Top, .Left, .Right, .Bottom] {
        let constraint = NSLayoutConstraint(item: self,
                                            attribute: attribute,
                                            relatedBy: .Equal,
                                            toItem: wrapper,
                                            attribute: attribute,
                                            multiplier: 1,
                                            constant: 0)
        if attribute == .Bottom { constraint.priority = 999 }
        wrapper.addConstraint(constraint)
    }
    return wrapper
}

Eklemek yerine stackView, kullanın stackView.wrapped().


1

İlk olarak, diğerlerinin önerdiği gibi, kontrol edebileceğiniz kısıtlamaların, yani UIStackView'a özgü kısıtlamaların öncelik 999'a ayarlanmadığından emin olun, böylece görünüm gizlendiğinde geçersiz kılınabilirler.

Hala sorunu yaşıyorsanız, sorun büyük olasılıkla gizli StackViews'taki boşluktan kaynaklanmaktadır. Çözümüm, boşluk bırakıcı olarak bir UIView eklemek ve UIStackView aralığını sıfıra ayarlamaktı. Daha sonra View.height veya View.width kısıtlamalarını (dikey veya yatay bir yığına bağlı olarak) StackView aralığına ayarlayın.

Ardından, yeni eklenen görünümlerinizin içerik sarılma ve içerik sıkıştırma direnci önceliklerini ayarlayın. Üst StackView dağıtımını da değiştirmeniz gerekebilir.

Yukarıdakilerin tümü Interface Builder'da yapılabilir. Ayrıca, yeni eklenen görünümlerin bazılarını programlı olarak gizlemeniz / göstermeniz gerekebilir, böylece istenmeyen boşluklar oluşmaz.


1

Son zamanlarda bir UIStackView. Bir sürü kitap tutma ve paketleme yığını yapmak yerine UIViews, kendim için bir çıkış parentStackViewve saklamak / göstermek istediğim çocuklar için bir çıkış yeri oluşturmayı seçtim .

@IBOutlet weak var parentStackView: UIStackView!
@IBOutlet var stackViewNumber1: UIStackView!
@IBOutlet var stackViewNumber2: UIStackView!

Film şeridinde, parentStack'im şöyle görünüyor:

görüntü açıklamasını buraya girin

4 çocuğu var ve her çocuğun içinde bir sürü yığın manzarası var. Bir yığın görünümünü gizlediğinizde, aynı zamanda yığın görünümleri olan UI öğeleri de varsa, bir otomatik düzen hataları akışı görürsünüz. Gizlemek yerine onları kaldırmayı seçtim.

Benim örnekte, parentStackViewsÜst Stack View, StackViewNumber1, Stack Görünüm Sayı 2 ve Durdur Düğmesi: 4 element dizisi içerir. Endeksleri arrangedSubviewssırasıyla 0, 1, 2 ve 3'tür. Birini gizlemek istediğimde, onu parentStackView's arrangedSubviewsdiziden kaldırırım . Zayıf olmadığı için hafızada kalır ve daha sonra istediğiniz dizine geri koyabilirsiniz. Yeniden başlatmıyorum, bu yüzden ihtiyaç duyulana kadar takılıyor ama hafızayı şişirmiyor.

Yani temelde şunları yapabilirsiniz ...

1) Ana yığınınız ve gizlemek / göstermek istediğiniz çocuklar için IBOutlets'i storyboard'a sürükleyin.

2) Onları gizlemek istediğinizde, parentStackView's arrangedSubviewsdiziden gizlemek istediğiniz yığını kaldırın .

3) Çağrı self.view.layoutIfNeeded()ile UIView.animateWithDuration.

Son iki stackView'in olmadığını unutmayın weak. Onları ortaya çıkardığın zaman etrafta tutmalısın.

StackViewNumber2'yi gizlemek istediğimi varsayalım:

parentStackView.removeArrangedSubview(stackViewNumber2)
stackViewNumber2.removeFromSuperview()

Sonra onu canlandırın:

UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Daha stackViewNumber2sonra "göstermek" istiyorsanız, bunu istediğiniz parentStackView arrangedSubViewsdizine ekleyebilir ve güncellemeyi canlandırabilirsiniz.

parentStackView.removeArrangedSubview(stackViewNumber1)
stackViewNumber1.removeFromSuperview()
parentStackView.insertArrangedSubview(stackViewNumber2, at: 1)

// Then animate it
UIView.animate(withDuration: 0.25,
               delay: 0,
               usingSpringWithDamping: 2.0,
               initialSpringVelocity: 10.0,
               options: [.curveEaseOut],
               animations: {
                self.view.layoutIfNeeded()
},
               completion: nil)

Bunun, kısıtlamalar üzerinde muhasebe yapmaktan, önceliklerle uğraşmaktan vb. Çok daha kolay olduğunu buldum.

Varsayılan olarak gizlenmesini istediğiniz bir şey varsa, bunu film şeridine yerleştirip kaldırabilir viewDidLoadve animasyonu kullanmadan güncelleyebilirsiniz view.layoutIfNeeded().


1

Gömülü Yığın Görünümlerinde de aynı hataları yaşadım, ancak çalışma zamanında her şey yolunda gitti.

isHidden = trueÜst yığın görünümünü gizlemeden önce tüm alt yığın görünümlerini gizleyerek (ayarlayarak ) kısıtlama hatalarını çözdüm .

Bunu yapmak, alt düzenlenmiş görünümleri kaldırmanın tüm karmaşıklığına sahip olmadı, onları tekrar eklemeye ihtiyaç duyduğunuzda bir indeksi korudu.

Bu yardımcı olur umarım.


1

Senseful, yukarıdaki sorunun kökenine mükemmel bir cevap verdi, bu yüzden doğrudan çözüme gideceğim.

Yapmanız gereken tek şey, tüm stackView kısıtlamaları önceliğini 1000'den daha düşük bir değere ayarlamaktır (999 iş yapar). Örneğin, stackView sol, sağ, üst ve alt denetim görünümüyle sınırlandırılmışsa, tüm 4 kısıtlamanın önceliği 1000'den düşük olmalıdır.


0

Belirli bir boyut sınıfıyla çalışırken bir kısıt oluşturmuş olabilirsiniz (ör. WCompact hRegular) ve sonra başka bir boyut sınıfına geçtiğinizde bir kopya oluşturmuş olabilirsiniz (ör. WAny hAny). Farklı boyut sınıflarındaki UI nesnelerinin kısıtlamalarını kontrol edin ve kısıtlamalarla ilgili anormallikler olup olmadığına bakın. çarpışan kısıtlamaları gösteren kırmızı çizgiler görmelisiniz. Maalesef 10 itibar puanı kazanana kadar resim koyamam: /



Evet, arayüz oluşturucudaki boyut sınıfları arasında geçiş yaparak kesinlikle herhangi bir kırmızı görmüyorum. Ben sadece "Herhangi" boyutunu kullandım.
Ben Guild

0

Bir seferde tüm UIStackView'ları gizlemek istedim, ancak OP ile aynı hataları alıyordum, bu benim için düzeltildi:

for(UIView *currentView in self.arrangedSubviews){
    for(NSLayoutConstraint *currentConstraint in currentView.constraints){
        [currentConstraint setPriority:999];
    }
}

Bu benim için işe yaramadı çünkü otomatik düzen motoru gerekli bir kısıtlamayı ( priority = 1000) gerekli değil ( ) olarak değiştirdiğinizde şikayet ediyor priority <= 999.
Duyarlı

0

Yükseklik kısıtlaması olan bir dizi düğmem vardı. Bu, bir düğme gizlendiğinde olur. Bu düğme yükseklik sınırlamasının önceliğini 999 olarak ayarlamak sorunu çözdü.


-2

Bu hatanın UIStackView ile ilgisi yoktur. Aynı önceliklere sahip çatışma kısıtlamalarınız olduğunda olur. Örneğin, bir kısıtlamanız, görünümünüzün genişliğinin 100 olduğunu belirtirse ve aynı zamanda başka bir kısıtınız varsa, görünümün genişliğinin kapsayıcısının% 25'i olduğunu belirtir. Açıktır ki, birbiriyle çelişen iki kısıt vardır. Çözüm, onları silmektir.


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.