NSLayoutConstraint “UIView-Encapsulated-Layout-Height” nedir ve onu temiz bir şekilde yeniden hesaplamaya zorlamaya nasıl devam etmeliyim?


262

Ben var UITableViewiOS 8 altında çalışan ve film şeridindeki sınırlamalardan otomatik hücre yükseklikleri kullanıyorum.

Hücrelerimden biri tek bir tane içeriyor UITextViewve kullanıcı girişine göre daralması ve genişletmesi gerekiyor - metni küçültmek / genişletmek için dokunun.

Metin görünümüne bir çalışma zamanı kısıtlaması ekleyerek ve kullanıcı olaylarına yanıt olarak kısıtlama sabitini değiştirerek bunu yapıyorum:

-(void)collapse:(BOOL)collapse; {

    _collapsed = collapse;

    if(collapse)
        [_collapsedtextHeightConstraint setConstant: kCollapsedHeight]; // 70.0
    else
        [_collapsedtextHeightConstraint setConstant: [self idealCellHeightToShowFullText]];

    [self setNeedsUpdateConstraints];

}

Ne zaman bunu yaparsam, ben tableViewgüncellemeler sarın ve arayın [tableView setNeedsUpdateConstraints]:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView setNeedsUpdateConstraints];
// I have also tried 
// [self.tableView reloadRowsAtIndexPaths:@[indexPath] withRowAnimation:UITableViewRowAnimationTop];
// with exactly the same results.

[tableView endUpdates];

Bunu yaptığımda, hücrem genişliyor (ve bunu yaparken canlandırıyor) ancak bir kısıtlama uyarısı alıyorum:

2014-07-31 13:29:51.792 OneFlatEarth[5505:730175] 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:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>",

    "<NSLayoutConstraint:0x7f94dced2260 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']-(15)-|   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced2350 V:|-(6)-[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...']   (Names: '|':UITableViewCellContentView:0x7f94de5773a0 )>",

    "<NSLayoutConstraint:0x7f94dced6480 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7f94de5773a0(91)]>"
 )

Will attempt to recover by breaking constraint 

<NSLayoutConstraint:0x7f94dced2b60 V:[UITextView:0x7f94d9b2b200'Brief text: Lorem Ipsum i...'(388)]>

388 benim hesaplanan yüksekliğim, UITextViewXcode / IB'deki diğer kısıtlamalar benimdir.

Sonuncusu beni rahatsız ediyor - UIView-Encapsulated-Layout-Heightilk oluşturulduğunda hücrenin hesaplanan yüksekliği olduğunu tahmin ediyorum - ( UITextViewboyumu> = 70.0 olarak ayarladım ) ancak bu türden bir kısıtlamanın daha sonra bir geçersiz kıldığı doğru görünmüyor güncellenmiş kullanıcı cnstraint.

Daha da kötüsü, düzen kodu yükseklik kısıtlamamı kırmaya çalıştığını söylese de, hücre yüksekliğini yeniden hesaplamaya devam ediyor ve her şey istediğim gibi çiziyor.

Öyleyse, nedir NSLayoutConstraint UIView-Encapsulated-Layout-Height(tahmin ediyorum ki otomatik hücre boyutlandırması için hesaplanan yükseklik) ve bunu temiz bir şekilde yeniden hesaplamaya zorlamaya nasıl devam etmeliyim?


3
çapraz Apple dev forumlarda gönderildi: devforums.apple.com/thread/238803
Rog

3
Benzer bir sorunu aşağıdaki şekilde çözdüm ve iOS 7/8'de çalışıyor. 1) Kısıtlama önceliklerinden birini 750'ye indirin. 1 veya 2'yi deneyeceğim 2) awakeFromNib setinde hücre alt sınıfında self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;. Otomatik boyutlandırma maskesinin başlangıçta ayarlanmasının son kısıtlamanın eklenmesini durdurduğunu düşünüyorum. Bu çözümü burada buldum: github.com/wordpress-mobile/WordPress-iOS/commit/…
Jesse

3
@RogerNolan, haber var mı? Arayüz Oluşturucu'da Otomatik düzen ile oynarken de aynı sorunu buldum. Bazı hücreler bu soruna neden olur, bazıları değil.
orkenstein

3
Otomatikleştirme işareti eklemenin iyi bir çözüm olduğunu düşünmüyorum.
Rog

1
@RogerNolan, bu düzen değişikliklerini gerçekleştirmeden önce bu hücreyi IB'de mi oluşturuyorsunuz? Aynı sorunu ayıklamaktayım ama fazladan kısıtlama eklemiyordum. Görüşümü sıfırdan yeniden oluşturarak uyarıyı bastırmayı başardım ve 2 film şeridi dosyasını yayınladığımda tek fark, uyarılardaki sürümün <rect key="frame" x="0.0" y="0.0" width="600" height="110"/>tanımındaki satırı eksik olması ve bunun bir IB hatası olduğuna inanmamı sağlamasıydı . En azından benimki zaten.
Ell Neal

Yanıtlar:


301

Önceliğinizi _collapsedtextHeightConstraint999'a düşürmeye çalışın . Bu şekilde sağlanan sistem UIView-Encapsulated-Layout-Heightkısıtlaması her zaman önceliklidir.

Ne geri döndüğünüzü temel alır -tableView:heightForRowAtIndexPath:. Doğru değeri, kendi kısıtınızı ve oluşturulan değeri aynı döndürdüğünüzden emin olun. Kendi kısıtlamanız için daha düşük öncelik, animasyonları daraltma / genişletme sırasında çakışmaları önlemek için yalnızca geçici olarak gereklidir.


74
Bu, istediğimin tam tersini başarırdı. UIView-Encapsulated-Layout-Height yanlış - önceki düzene ait.
Rog

7
UIView-Encapsulated-Layout-HeightYükseklikleri belirlenir kez kısıtlama UITableView ilave edilir. systemLayoutSizeFittingSizeContentView dayalı yüksekliği hesaplamak . Burada UIView-Encapsulated-Layout-Heightönemli değil. Daha sonra tableView, contentSize öğesini döndürülen değere açıkça ayarlar heightForRowAtIndexPath:. Bu durumda, rowHeights hesaplandıktan sonra tableView kısıtlamasının önceliği olması gerektiği için özel kısıtlarımızın önceliğini düşürmek doğrudur.
Ortwin Gentz

8
@OrtwinGentz: Özel görünümlerimizin önceliğini düşürmek için doğru olan noktayı yine de anlamıyorum çünkü rowHeights hesaplandıktan sonra tableView kısıtlamasının önceliği olması gerekir . Şey yani UIView-Encapsulated-Layout-Height... Ben önceliklerini düşürün yoksa yanlış
test

38
Bunun gerçek sorunu çözmemekle çatışmayı önlediğini düşünüyorum - bu hala bir Apple hatası gibi hissediyor olsa da, muhtemelen yapılacak doğru şey olduğunu düşünüyorum. Özellikle Apple'ın bu kısıtlamayı yeniden hesaplaması ve hata yazdırıldıktan sonra her şey doğru şekilde düzenlenmesi gerçeği ışığında.
Rog

9
Eklenen kısıtlamanın doğru olduğunu varsaydığı için bu cevap için -1. UIView-Encapsulated-Layout-WidthBenim durumumda eklenen sadece yanlış, ama çalışma zamanında açık kısıtlamalar benim tercih gibi görünüyor.
ray

68

Benzer bir senaryo var: UILabel nesnelerin birkaç satır olduğu bir satır hücre ile bir tablo görünümü. İOS 8 ve otomatik yerleşim kullanıyorum.

Döndüğümde yanlış hesaplanmış satır yüksekliği aldım (43.5 gerçek yükseklikten çok daha az). Şöyle görünüyor:

"<NSLayoutConstraint:0x7bc2b2c0 'UIView-Encapsulated-Layout-Height' V:[UITableViewCellContentView:0x7bc37f30(43.5)]>"

Bu sadece bir uyarı değil. Tablo görünümü hücresimin düzeni korkunç - tüm metinler bir metin satırında çakışıyor.

Aşağıdaki satırın benim sorunum sihirli bir şekilde "düzeltme" beni şaşırtıyor (autolayout hiçbir şey şikayet ve ben ekranda ne bekliyorum olsun):

myTableView.estimatedRowHeight = 2.0; // any number but 2.0 is the smallest one that works

bu çizgi ile veya bu çizgi olmadan:

myTableView.rowHeight = UITableViewAutomaticDimension; // by itself this line only doesn't help fix my specific problem

8
En sonunda! Bu doğru bir cevap. WWDC oturumunda, otomatik satır yüksekliği boyutlandırmasını kullanacaksanız, tahmini bir Satır Yüksekliği ayarlayacaksınız veya kötü şeyler meydana gelecektir. (Evet, Apple gerçekten kötü şeyler oldu)
Abdalrahman Shatou

50
FWIW, tahminin eklenmesi benim için hiçbir fark yaratmadı.
Benjohn

3
Heh. Yine aynı cevaba döndüm ve neşeyle ve umutla uygulamaya gittim. Bir kez daha, benim için fark
yaratmadı

3
TableView tarafından döndürülen tahminler: tahminiHeightForRowAtIndexPath: En azından hücre kadar büyük OLMALIDIR . Aksi takdirde, tablonun hesaplanan yüksekliği gerçek yükseklikten daha az olacaktır ve tablo yukarı kayabilir (örneğin gevşetme sekmesi tabloya döndükten sonra). UITableViewAutomaticDimension kullanılmamalıdır.
Matt

1
Başlangıçta ne kadar düşük olursa estimatedRowHeighto kadar sık cellForRowAtIndexPathçağrılacağını unutmayın. Tablo görüntüleme yüksekliğinin kesin olması için tahminiRowHeight sürelerine bölünür. 12 inçlik bir iPad Pro'da, bu potansiyel olarak binlercede bir sayı olabilir ve veri kaynağını çekiçleyecek ve önemli bir gecikmeye neden olabilir.
Mojo66

32

Uyarı mesajlarının kırılması gerektiğini söylediği kısıtlamadaki değerlerden birine öncelik vererek belirleme uyarısı alabildim (aşağıda "Will attempt to recover by breaking constraint"). Önceliği daha büyük bir şeye ayarladığım sürece 49uyarı ortadan kalkar.

Benim için bu, kısıtlamamı değiştirmek anlamına geliyordu, uyarı kırılmaya çalıştığını söyledi:

@"V:|[contentLabel]-[quoteeLabel]|"

için:

@"V:|-0@500-[contentLabel]-[quoteeLabel]|"

Aslında, bu kısıtlamanın herhangi bir unsuruna bir öncelik ekleyebilirim ve işe yarayacaktır. Hangisinin olduğu önemli değil. Hücrelerim uygun yüksekliğe ulaştı ve uyarı görüntülenmiyor. Örneğin, Roger, yükseklik değeri kısıtlamasından @500hemen sonra eklemeyi deneyin 388(ör. 388@500).

Bunun neden işe yaradığından tam olarak emin değilim ama biraz araştırma yaptım. In NSLayoutPriority enum , şunlar anlaşılmaktadır NSLayoutPriorityFittingSizeCompressionöncelik seviyesidir 50. Bu öncelik seviyesine ilişkin belgeler şunları söylüyor:

Bir görünüme fittingSize iletisi gönderdiğinizde, görünümün içeriği için yeterince büyük olan en küçük boyut hesaplanır. Bu, görünümün bu hesaplamada mümkün olduğunca küçük olmasını istediği öncelik düzeyidir. Oldukça düşük. Genellikle tam olarak bu öncelikte bir kısıtlama yapmak uygun değildir. Daha yüksek veya daha düşük olmak istiyorsunuz.

Dokümantasyon başvurulan için fittingSizemesajın okur:

Görüntülemenin sahip olduğu kısıtlamaları karşılayan minimum boyutu. (Sadece oku)

AppKit, bu özelliği, görünümünün ve alt görünümlerinin sahip olduğu tüm kısıtlamaları göz önünde bulundurarak ve görünümü olabildiğince küçük yapma tercihini karşılayarak görünüm için mevcut en iyi boyuta ayarlar. Bu özellikteki boyut değerleri hiçbir zaman negatif değildir.

Bunun ötesine geçmedim, ancak sorunun nerede yattığıyla ilgili bir anlamı olduğu anlaşılıyor.


Jeff. Hala bana bir böcek gibi geliyor. Apple rdar'a henüz cevap vermedi :-(
Rog

13

Zamanın% 99,9'u, özel hücreler veya başlıklar UITableViewskullanırken, tablo ilk kez yüklendiğinde tüm çakışmalar oluşur. Yüklendikten sonra, çatışmayı bir daha göremezsiniz.

Bunun nedeni, çoğu geliştiricinin hücre / üstbilgideki bir öğeyi düzenlemek için genellikle bir tür sabit yükseklik veya bağlantı kısıtlaması kullanmasıdır. Çatışma, UITableViewilk yükler / ortaya konulduğunda, hücrelerinin yüksekliğini 0'a ayarladığı için oluşur. Bu, açıkça kendi kısıtlarınızla çelişir. Bunu çözmek için, sabit yükseklik kısıtlamalarını daha düşük bir önceliğe ( .defaultHigh) ayarlamanız yeterlidir . Konsol mesajını dikkatlice okuyun ve yerleşim sisteminin hangi kısıtlamayı kırmaya karar verdiğini görün. Genellikle önceliğinin değişmesi gereken budur. Önceliği şu şekilde değiştirebilirsiniz:

let companyNameTopConstraint = companyNameLabel.topAnchor.constraint(equalTo: companyImageView.bottomAnchor, constant: 15)
    companyNameTopConstraint.priority = .defaultHigh

NSLayoutConstraint.activate([
            companyNameTopConstraint,
           the rest of your constraints here
            ])

1
Güzel açıklama.
Glenn

12

Ben sahte kaldırarak bu hatayı gidermek başardı cell.layoutIfNeeded()benim sahip oldukları tableView'ın cellForRowAtyöntemiyle.


1
Evet, bu da benim için sorunu çözdü. Başlangıçta bir şey özleyebileceğimi düşündüm bu yüzden kod düzeni kontrendikleri yapıyordum. Teşekkürler
John

1
Aynı şey! Teşekkürler!
Andrey Chernukha

7

Tablo görünümünü kısıtlamalarını güncellemesi için bilgilendirmek yerine hücreyi yeniden yüklemeyi deneyin:

[tableView beginUpdates];

[_briefCell collapse:!_showFullBriefText];

[tableView reloadRowsAtIndexPaths:@[[tableView indexPathForCell:_briefCell]] withRowAnimation:UITableViewRowAnimationNone];

[tableView endUpdates];

UIView-Encapsulated-Layout-Height muhtemelen tablo görünümünün, hücrenin o andaki kısıtlamalarına bağlı olarak, ilk yükleme sırasında hücre için hesapladığı yüksekliktir.


Sorumda söylemeliydim, bunu denedim ve işe yaramıyor. UIView-Encapsulated-Layout-Height
Rog

6
En azından bir cevap göndermek için ödülü al. SO, aksi takdirde buharlaşmasına izin verecek gibi görünüyor.
Rog

6

Bir başka olasılık:

Hücre yüksekliğini (contentView yüksekliği, çoğu zaman aşağıdaki gibi) hesaplamak için otomatik mizanpaj kullanıyorsanız ve uitableview ayırıcısı varsa, hücre yüksekliğine geri dönmek için ayırıcı yüksekliğini eklemeniz gerekir. Doğru yüksekliği elde ettikten sonra, bu otomatik uyarı uyarısı almayacaksınız.

- (CGFloat)calculateHeightForConfiguredSizingCell:(UITableViewCell *)sizingCell {
   [sizingCell setNeedsLayout];
   [sizingCell layoutIfNeeded];
   CGSize size = [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize];
   return size.height; // should + 1 here if my uitableviewseparatorstyle is not none

}

Bu, sadece bazı simülatörlerde (iPad 6 Plus) oldukça karmaşık bir hücre düzeninde düzen yüksekliği belirsizlikleri aldığım bir durumda bana yardımcı oldu. Bana öyle geliyor ki, bazı iç yuvarlama hataları nedeniyle içerik biraz sıkılıyor ve kısıtlamalar sıkılığa kavuştuğumdan daha sıkılmaya hazır değilse. Yani döndürmek yerine UITableViewAutomaticDimensionde heightForRowAtIndexPathI dönmek [sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize] + 0.1
Leo

Demek istediğim[sizingCell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height + 0.1
Leo

staggeringly; ayırıcıyı kaldırmak masamın davrandığı şey, bu yüzden teşekkürler.
royalmurder

5

Jesse'nin söz konusu yorumda belirtildiği gibi , bu benim için çalışıyor:

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight;

Bilginize, bu sorun iOS 10'da oluşmaz.


2
Swift 4.2'de: self.contentView.autoresizingMask = [.f FlexibleHeight]
airowe

4

UITableViewAutomaticDimension kullanırken ve hücre içindeki bir görünümde bir yükseklik kısıtlaması değiştirirken bu hata vardı.

Sonunda bunun sınır sabit değerinin en yakın tamsayıya yuvarlanmamasından kaynaklandığını anladım.

let neededHeight = width / ratio // This is a CGFloat like 133.2353
constraintPictureHeight.constant = neededHeight // Causes constraint error
constraintPictureHeight.constant = ceil(neededHeight) // All good!

2
Beni sorunumla ilgili yorum bu oldu. Dinamik olarak yükleyen bir görüntü hücresi var (büyür ve küçülür) ve tahmini bir tablo görünümü ile tahmin edilenRowHeight = 50 ve rowHeight = UITableViewAutomaticDimension. Tableview doğru yükseklik olmasına rağmen hala kısıtlamalar kırıyordu. Ayırıcıların 0.3333 yüksekliğinde olduğu ve kırılacak hücre içindeki görüntü boyutu kısıtlamamı tekmelediği ortaya çıktı. Separatörleri kapattıktan sonra her şey iyiydi. Bana neyi arayacağın için teşekkürler Che.
migs647

1
Bu durumda fazladan bir kısıt oluşturun, örneğin, bottomMargin> = view.bottomMargin+1@900. AutoLayout fazladan 1 noktayı barındırmaya çalışır, hücreyi yeniden boyutlandırır, ayırıcı yüksekliği nedeniyle karışır, bazı kısıtlamaları kırmaya / gevşetmeye çalışır, @ @ 900'ü bulur ve bunu atar. İstediğiniz düzeni uyarılar olmadan alırsınız.
Anton

günümü kurtar. neyse birkaç saat
Anton Tropashko

1

Metin görünümünü içeriğine uyacak şekilde boyutlandırma ve yükseklik sınırlama sabitini ortaya çıkan yüksekliğe güncelleme, UIView-Encapsulated-Layout-Heightbenim için kısıtlama çatışmasını düzeltti , örneğin:

[self.textView sizeToFit];
self.textViewHeightConstraint.constant = self.textView.frame.size.height;

Bunu nerede yaptın? LayoutSubviews?
Mart'ta stuckj

1

Bu hata ile başımı kaşıp birkaç saat geçirdikten sonra sonunda benim için çalışan bir çözüm buldum. benim asıl sorun farklı hücre tipleri için kayıtlı birden fazla uç vardı ama bir hücre tipi özellikle farklı boyutlara izin verildi (o hücrenin tüm örnekleri aynı boyutta olacak). bu yüzden, tablo görünümü bu tür bir hücreyi ayıklamaya çalışırken ortaya çıktı ve farklı bir yüksekliğe sahip oldu. Ayarlayarak çözdüm

self.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; self.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

hücre boyutunu hesaplamak için verisine sahip olduğunda. İçinde olabileceğini düşünüyorum

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath

gibi bir şey

cell.contentView.autoresizingMask = UIViewAutoresizingFlexibleHeight; cell.frame = CGRectMake(0, 0, self.frame.size.width, {correct height});

Bu yardımcı olur umarım!


0

TableView temsilcisinden indexPath hücresi için yükseklik olsun. sonra şu hücreyi alın cellForRowAtIndexPath:

top (10@1000)
    cell
bottom (0@1000)

cell.contentView.height: 0 // <-> (UIView-Encapsulated-Layout-Height: 0 @ 1000) üst (10 @ 1000) (UIView-Encapsulated-Layout-Height: 0 @ 1000) ile çakışırsa,

çünkü öncelikleri eşittir 1000'dir UIView-Encapsulated-Layout-Height.


0

Böyle bir mesaj alıyordum:

Aynı anda ... kısıtlamaları karşılamak için açılamıyor
...
...
...
0x7fe74bdf7e50 'UIView-Encapsulated-Düzen-Boy' V: [UITableViewCellContentView: 0x7fe75330c5c0 (21,5)] NSLayoutConstraint
...
...
tarafından kurtarmak denemesi yapılacak sınırlamayı aşma NSLayoutConstraint: 0x7fe0f9b200c0 UITableViewCellContentView: 0x7fe0f9b1e090.bottomMargin == UILabel: 0x7fe0f9b1e970.bottom

Ben özel bir kullanıyorum UITableViewCellile UITableViewAutomaticDimensionyükseklik için. Ayrıca estimatedHeightForRowAtIndex:yöntemi de uyguladım .

Bana sorun çıkaran kısıtlama şuna benziyordu

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-[title]-|" options:0 metrics:nil views:views];

Bu kısıtlamayı değiştirmek sorunu çözecektir, ancak başka bir yanıt gibi, bunun gerekli olmasını istediğim bir kısıtlamanın önceliğini düşürdüğü için bunun doğru olmadığını hissettim:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6@999-[title]-6@999-|" options:0 metrics:nil views:views];

Ancak, fark ettiğim şey, aslında sadece önceliği kaldırırsam, bu da işe yarıyor ve kırılma kısıtlama günlüklerini alamıyorum:

[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-6-[title]-6-|" options:0 metrics:nil views:views];

Bu |-6-[title]-6-|ve arasındaki farkın bir sırrı |-[title-|. Ancak boyutu belirtmek benim için bir sorun değil ve günlüklerden kurtuluyor ve gerekli kısıtlamalarımın önceliğini düşürmem gerekmiyor.


0

Bunu ayarla view.translatesAutoresizingMaskIntoConstraints = NO;, bu sorunu çözmelidir.


Bu mükemmel bir çözüm olmasa bile benim için mükemmel çalıştı.
Enkha

0

Koleksiyon görüntüleme hücresi ile benzer bir sorun vardı.

Hücrenin tabanına (zincirin sonuncusu görünümün üstünden altına kadar olan son kısıtlamanın önceliğini düşürerek çözdüm - sonuçta yüksekliğini belirleyen şey budur) 999.

Hücrenin yüksekliği doğruydu ve uyarılar kayboldu.

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.