TL; DR: Okumayı sevmiyor musunuz? Doğrudan GitHub'daki örnek projelere atlayın:
Kavramsal Açıklama
Aşağıdaki ilk 2 adım, hangi iOS sürümleri için geliştirdiğinizden bağımsız olarak geçerlidir.
1. Kısıtlamalar Ayarla ve Ekle
Senin içinde UITableViewCell
hücrenin subviews hücrenin kenarlarına sabitlenmiş onların kenarları, böylece alt sınıf, kısıtlamaları eklemek contentView (en önemlisi üst hem de alt kenarlarına). NOT: alt görünümleri hücrenin kendisine sabitlemeyin; sadece hücrenin contentView
! Bu alt görüntülerin gerçek içerik boyutunun, her bir alt görünüm için dikey boyuttaki içerik sıkıştırma direncinin ve içerik sarılma kısıtlamalarının eklediğiniz daha yüksek öncelikli kısıtlamalarla geçersiz kılınmamasını sağlayarak tablo görünümü hücresinin içerik görünümünün yüksekliğini artırmasına izin verin . ( Ha? Buraya tıklayın. )
Unutmayın, fikir, hücrenin alt görünümlerinin hücrenin içerik görünümüne dikey olarak bağlanmasıdır, böylece "baskı uygulayabilir" ve içerik görünümünü onlara uyacak şekilde genişletebilirler. Birkaç alt görünüme sahip örnek bir hücre kullanarak , kısıtlamalarınızın bazılarının (hepsi değil!) Neye benzemesi gerektiğini gösteren görsel bir örnek :
Yukarıdaki örnek hücredeki çok satırlı gövde etiketine daha fazla metin eklendikçe, metni sığdırmak için dikey olarak büyümesi gerekeceğini ve bu da hücrenin yükseklikte büyümesini zorlayacağını hayal edebilirsiniz. (Elbette, bunun doğru bir şekilde çalışması için kısıtlamaları doğru yapmanız gerekir!)
Kısıtlamalarınızı doğru bir şekilde yapmak, dinamik hücre yüksekliklerini Otomatik Yerleşim ile çalışmanın kesinlikle en zor ve en önemli parçasıdır . Burada bir hata yaparsanız, her şeyin çalışmasını engelleyebilir - bu yüzden acele etmeyin! Hangi kısıtlamaların nereye eklendiğini tam olarak bildiğiniz için kısıtlamalarınızı kodda ayarlamanızı öneririz ve işler ters gittiğinde hata ayıklamak çok daha kolaydır. Kodda kısıtlama eklemek, yerleşim bağlantılarını veya GitHub'da bulunan harika açık kaynak API'lerinden birini kullanarak Interface Builder kadar kolay ve önemli ölçüde daha güçlü olabilir.
- Kodda kısıtlamalar ekliyorsanız, bunu bir kez
updateConstraints
UITableViewCell alt sınıfınızın yöntemi içinden yapmanız gerekir . updateConstraints
Bir kereden fazla çağrılabileceğini unutmayın , bu nedenle aynı kısıtlamaları bir kereden fazla eklemekten kaçınmak için, kısıtlama kodunuzu ( updateConstraints
örneğin didSetupConstraints
, kısıtlamanızı çalıştırdıktan sonra EVET olarak ayarladığınız) -kod eklendikten sonra). Öte yandan, mevcut kısıtlamaları güncelleyen bir kodunuz varsa ( constant
bazı kısıtlamalarda özelliği ayarlamak gibi ), yöntemin her çağrıldığında çalışabilmesi updateConstraints
için didSetupConstraints
bunu denetimin içine ama dışına yerleştirin .
2. Benzersiz Tablo Görünümü Hücre Yeniden Tanımlama Tanımlayıcılarını Belirleme
Hücredeki her benzersiz sınırlama kümesi için benzersiz bir hücre yeniden kullanım tanımlayıcısı kullanın. Başka bir deyişle, hücreleriniz birden fazla benzersiz düzene sahipse, her benzersiz düzen kendi yeniden kullanım tanımlayıcısını almalıdır. (Yeni bir yeniden kullanım tanımlayıcısı kullanmanız için iyi bir ipucu, hücre değişkeninizin farklı sayıda alt görünümü olduğunda veya alt görünümlerin farklı bir şekilde düzenlenmiş olmasıdır.)
Örneğin, her hücrede bir e-posta mesajı görüntülüyorsanız, 4 benzersiz mizanpajınız olabilir: yalnızca konu içeren mesajlar, konu ve gövde içeren mesajlar, konu ve mesaj ekli mesajlar ve konu içeren mesajlar, gövde ve fotoğraf eki. Her düzen, bunu başarmak için tamamen farklı kısıtlamalara sahiptir, bu nedenle hücre başlatıldıktan ve bu hücre türlerinden biri için kısıtlamalar eklendiğinde, hücre, bu hücre türüne özgü benzersiz bir yeniden kullanım tanımlayıcısı almalıdır. Bu, bir hücreyi yeniden kullanmak üzere ayırdığınızda, kısıtlamalar zaten eklenmiş ve bu hücre türüne gitmeye hazır olduğunuz anlamına gelir.
İçsel içerik boyutundaki farklılıklar nedeniyle, aynı kısıtlamalara (tip) sahip hücrelerin yine de değişen yükseklikleri olabilir! Farklı içerik boyutları nedeniyle temel olarak farklı düzenleri (farklı kısıtlamalar) farklı hesaplanmış görünüm çerçeveleriyle (özdeş kısıtlamalardan çözülmüş) karıştırmayın.
- Aynı yeniden kullanım havuzuna tamamen farklı kısıtlama kümelerine sahip hücreler eklemeyin (örn. Aynı yeniden kullanım tanımlayıcısını kullanın) ve ardından eski kısıtlamaları kaldırmaya ve her bir ayrışmadan sonra sıfırdan yeni kısıtlamalar oluşturmaya çalışmayın. Dahili Otomatik Yerleşim motoru, kısıtlamalardaki büyük ölçekli değişiklikleri ele alacak şekilde tasarlanmamıştır ve büyük performans sorunları görürsünüz.
İOS 8 için - Kendiliğinden Boyutlanan Hücreler
3. Satır Yüksekliği Tahminini Etkinleştir
Kendi kendine boyutlandırma tablo görünümü hücrelerini etkinleştirmek için, tablo görünümünün rowHeight özelliğini UITableViewAutomaticDimension olarak ayarlamanız gerekir. TahminiRowHeight özelliğine de bir değer atamalısınız. Bu özelliklerin her ikisi de ayarlanır ayarlanmaz, sistem satırın gerçek yüksekliğini hesaplamak için Otomatik Düzen'i kullanır
Apple: Kendinden Boyutlandırmalı Masa Görünümü Hücreleriyle Çalışma
İOS 8 ile Apple, daha önce iOS 8'den önce uygulamanız gereken işlerin çoğunu içselleştirdi: Kendiliğinden boyutlanan hücre mekanizmasının çalışmasına izin vermek için rowHeight
, önce tablo görünümündeki özelliği sabit olarak ayarlamanız gerekir. UITableViewAutomaticDimension
. Ardından, tablo görünümünün estimatedRowHeight
özelliğini sıfır olmayan bir değere ayarlayarak satır yüksekliği tahminini etkinleştirmeniz yeterlidir , örneğin:
self.tableView.rowHeight = UITableViewAutomaticDimension;
self.tableView.estimatedRowHeight = 44.0; // set to whatever your "average" cell height is
Bunun yaptığı, tablo görünümüne henüz ekranda olmayan hücrelerin satır yükseklikleri için geçici bir tahmin / yer tutucu sağlamaktır. Daha sonra, bu hücreler ekranda kaydırılmak üzereyken, gerçek satır yüksekliği hesaplanacaktır. Her satır için gerçek yüksekliği belirlemek için, tablo görünümü her hücreye contentView
içerik görünümünün bilinen sabit genişliğine (tablo görünümünün genişliğine dayanarak, bir bölüm dizini gibi ek şeyler eklenmesi) dayanması gereken yüksekliği otomatik olarak sorar. veya aksesuar görünümü) ve hücrenin içerik görünümüne ve alt görünümlerine eklediğiniz otomatik mizanpaj kısıtlamaları. Bu gerçek hücre yüksekliği belirlendikten sonra, satır için eski tahmini yükseklik yeni gerçek yükseklikle güncellenir (ve tablo görünümünün contentSize / contentOffset öğesinde yapılan tüm değişiklikler sizin için gerektiği gibi yapılır).
Genel olarak, sağladığınız tahminin çok doğru olması gerekmez - yalnızca kaydırma göstergesini tablo görünümünde doğru bir şekilde boyutlandırmak için kullanılır ve tablo görünümü, yanlış göstergeler için kaydırma göstergesini sizin gibi ayarlamak için iyi bir iş çıkarır. ekran üzerinde kaydırma hücreleri. Sen koymalıdır estimatedRowHeight
tablo (görünümünde mülk viewDidLoad
"ortalama" satır yüksekliği sabit bir değere veya benzeri). Yalnızca sıra yükseklikleriniz aşırı değişkenliğe sahipse (örn. Büyüklük sırasına göre farklılık gösterir) ve tableView:estimatedHeightForRowAtIndexPath:
her bir satır için daha doğru bir tahmin döndürmek için gereken minimum hesaplamayı yapmak için uygulamayı yaparken, kaydırma sırasında kaydırma göstergesinin "atlama" olduğunu fark ederseniz .
İOS 7 desteği için (otomatik hücre boyutlandırmasını kendiniz uygulama)
3. Düzen Geçişi Yapın ve Hücre Yüksekliğini Alın
İlk olarak, kesinlikle yükseklik hesaplamaları için kullanılan her yeniden kullanım tanımlayıcısı için bir örnek olan bir tablo görünümü hücresinin bir ekran dışı örneğini başlatın . (Hücre başvurusunun, görünüm denetleyicisindeki bir özellik / ivar'da depolandığı ve hiçbir zaman tableView:cellForRowAtIndexPath:
tablo görünümünün gerçekten ekranda görüntülenmesi için döndürülmediği anlamına gelir .) Ardından, hücrenin tam içerikle yapılandırılması gerekir (örn. Metin, resimler vb.). tablo görünümünde görüntülenecekse tutacağını unutmayın.
Ardından, hücreyi hemen alt görünümlerini düzenlemeye zorlayın ve ardından hücrenin gerekli yüksekliğinin ne olduğunu bulmak systemLayoutSizeFittingSize:
için UITableViewCell
's yöntemini kullanın contentView
. UILayoutFittingCompressedSize
Hücrenin tüm içeriğine sığması için gereken en küçük boyutu elde etmek için kullanın . Yükseklik daha sonra tableView:heightForRowAtIndexPath:
delege yönteminden döndürülebilir .
4. Tahmini Satır Yüksekliklerini Kullanma
Tablo görünümünüzde birkaç düzineden fazla satır varsa, Otomatik Yükleme kısıtlama çözme işleminin, ilk yükleme sonrasında tableView:heightForRowAtIndexPath:
her bir satırda çağrıldığı gibi , tablo görünümünü ilk yüklerken ana iş parçacığını hızla aşağı çekebileceğini göreceksiniz ( kaydırma göstergesinin boyutunu hesaplamak için).
İOS 7'den itibaren estimatedRowHeight
özelliği tablo görünümünde kullanabilirsiniz (ve kesinlikle kullanmalısınız) . Bunun yaptığı, tablo görünümüne henüz ekranda olmayan hücrelerin satır yükseklikleri için geçici bir tahmin / yer tutucu sağlamaktır. Daha sonra, bu hücreler ekranda kaydırılmak üzereyken, gerçek satır yüksekliği (çağrılarak tableView:heightForRowAtIndexPath:
) hesaplanacak ve tahmini yükseklik gerçek hücre ile güncellenecektir.
Genel olarak, sağladığınız tahminin çok doğru olması gerekmez - yalnızca kaydırma göstergesini tablo görünümünde doğru bir şekilde boyutlandırmak için kullanılır ve tablo görünümü, yanlış göstergeler için kaydırma göstergesini sizin gibi ayarlamak için iyi bir iş çıkarır. ekran üzerinde kaydırma hücreleri. Sen koymalıdır estimatedRowHeight
tablo (görünümünde mülk viewDidLoad
"ortalama" satır yüksekliği sabit bir değere veya benzeri). Yalnızca sıra yükseklikleriniz aşırı değişkenliğe sahipse (örn. Büyüklük sırasına göre farklılık gösterir) ve tableView:estimatedHeightForRowAtIndexPath:
her bir satır için daha doğru bir tahmin döndürmek için gereken minimum hesaplamayı yapmak için uygulamayı yaparken, kaydırma sırasında kaydırma göstergesinin "atlama" olduğunu fark ederseniz .
5. (Gerekirse) Satır Yüksekliği Önbelleği Ekleme
Yukarıdakilerin tümünü yaptıysanız ve kısıtlama çözümü yaparken performansın kabul edilemez derecede yavaş olduğunu düşünüyorsanız tableView:heightForRowAtIndexPath:
, maalesef hücre yükseklikleri için bazı önbellekleme uygulamanız gerekir. (Bu, Apple mühendisleri tarafından önerilen yaklaşımdır.) Genel fikir, Autolayout motorunun kısıtlamaları ilk kez çözmesine izin vermek, ardından o hücre için hesaplanan yüksekliği önbelleğe almak ve o hücrenin yüksekliği için gelecekteki tüm istekler için önbelleğe alınan değeri kullanmaktır. Elbette hile, hücrenin yüksekliğinin değişmesine neden olabilecek herhangi bir şey olduğunda bir hücrenin önbelleğe alınmış yüksekliğini temizlediğinizden emin olmaktır - öncelikle, bu hücrenin içeriği değiştiğinde veya diğer önemli olaylar gerçekleştiğinde (kullanıcı ayarlaması gibi) Dinamik Tür metin boyutu kaydırıcısı).
iOS 7 Genel Örnek Kodu (birçok sulu yorum ile)
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path, depending on the particular layout required (you may have
// just one, or may have many).
NSString *reuseIdentifier = ...;
// Dequeue a cell for the reuse identifier.
// Note that this method will init and return a new cell if there isn't
// one available in the reuse pool, so either way after this line of
// code you will have a cell with the correct constraints ready to go.
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdentifier];
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// If you are using multi-line UILabels, don't forget that the
// preferredMaxLayoutWidth needs to be set correctly. Do it at this
// point if you are NOT doing it within the UITableViewCell subclass
// -[layoutSubviews] method. For example:
// cell.multiLineLabel.preferredMaxLayoutWidth = CGRectGetWidth(tableView.bounds);
return cell;
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Determine which reuse identifier should be used for the cell at this
// index path.
NSString *reuseIdentifier = ...;
// Use a dictionary of offscreen cells to get a cell for the reuse
// identifier, creating a cell and storing it in the dictionary if one
// hasn't already been added for the reuse identifier. WARNING: Don't
// call the table view's dequeueReusableCellWithIdentifier: method here
// because this will result in a memory leak as the cell is created but
// never returned from the tableView:cellForRowAtIndexPath: method!
UITableViewCell *cell = [self.offscreenCells objectForKey:reuseIdentifier];
if (!cell) {
cell = [[YourTableViewCellClass alloc] init];
[self.offscreenCells setObject:cell forKey:reuseIdentifier];
}
// Configure the cell with content for the given indexPath, for example:
// cell.textLabel.text = someTextForThisCell;
// ...
// Make sure the constraints have been set up for this cell, since it
// may have just been created from scratch. Use the following lines,
// assuming you are setting up constraints from within the cell's
// updateConstraints method:
[cell setNeedsUpdateConstraints];
[cell updateConstraintsIfNeeded];
// Set the width of the cell to match the width of the table view. This
// is important so that we'll get the correct cell height for different
// table view widths if the cell's height depends on its width (due to
// multi-line UILabels word wrapping, etc). We don't need to do this
// above in -[tableView:cellForRowAtIndexPath] because it happens
// automatically when the cell is used in the table view. Also note,
// the final width of the cell may not be the width of the table view in
// some cases, for example when a section index is displayed along
// the right side of the table view. You must account for the reduced
// cell width.
cell.bounds = CGRectMake(0.0, 0.0, CGRectGetWidth(tableView.bounds), CGRectGetHeight(cell.bounds));
// Do the layout pass on the cell, which will calculate the frames for
// all the views based on the constraints. (Note that you must set the
// preferredMaxLayoutWidth on multiline UILabels inside the
// -[layoutSubviews] method of the UITableViewCell subclass, or do it
// manually at this point before the below 2 lines!)
[cell setNeedsLayout];
[cell layoutIfNeeded];
// Get the actual height required for the cell's contentView
CGFloat height = [cell.contentView systemLayoutSizeFittingSize:UILayoutFittingCompressedSize].height;
// Add an extra point to the height to account for the cell separator,
// which is added between the bottom of the cell's contentView and the
// bottom of the table view cell.
height += 1.0;
return height;
}
// NOTE: Set the table view's estimatedRowHeight property instead of
// implementing the below method, UNLESS you have extreme variability in
// your row heights and you notice the scroll indicator "jumping"
// as you scroll.
- (CGFloat)tableView:(UITableView *)tableView estimatedHeightForRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do the minimal calculations required to be able to return an
// estimated row height that's within an order of magnitude of the
// actual height. For example:
if ([self isTallCellAtIndexPath:indexPath]) {
return 350.0;
} else {
return 40.0;
}
}
Örnek Projeler
Bu projeler, UILabels'de dinamik içerik içeren tablo görünümü hücreleri nedeniyle değişken satır yüksekliğine sahip tablo görünümlerinin tam olarak çalışan örnekleridir.
Xamarin (C # /. NET)
Xamarin kullanıyorsanız, @KentBoogaart tarafından bir araya getirilen bu örnek projeye göz atın .