Özel Tablo Görünümü Bölüm Üstbilgilerini ve Altbilgilerini Storyboard ile Uygulama


176

Bir film şeridi kullanmadan UIView, tuvali üzerine sürükleyebilir , yerleştirebilir ve sonra tableView:viewForHeaderInSectionveya tableView:viewForFooterInSectiontemsilci yöntemlerine yerleştirebiliriz.

Bunu bir UIView'i tuval üzerine sürükleyemediğimiz bir StoryBoard ile nasıl başarabiliriz?

Yanıtlar:


91

Bu sorunun iOS 5 için olduğunu biliyorum, ancak gelecekteki okuyucuların yararına, şimdi dequeueReusableHeaderFooterViewWithIdentifieryerine etkili iOS 6 kullanabileceğimizi unutmayın dequeueReusableCellWithIdentifier.

Yani viewDidLoad, ya registerNib:forHeaderFooterViewReuseIdentifier:ya arayın registerClass:forHeaderFooterViewReuseIdentifier:. Ardından viewForHeaderInSection, arayın tableView:dequeueReusableHeaderFooterViewWithIdentifier:. Bu API ile hücre prototipi kullanmıyorsunuz (NIB tabanlı görünüm veya programlı olarak oluşturulmuş görünüm), ancak bu, atanmamış üstbilgiler ve altbilgiler için yeni API'dir.


13
sigh Bir proto hücre yerine bir NIB kullanma ile ilgili bu iki temiz cevap şu anda 5 oy altında olduğunu ve "proto hücreleri ile hack" cevabının 200 üzerinde olduğunu
utanç verici.

5
Aradaki fark, Tieme'nin hacklenmesi ile tasarımınızı aynı film şeridinde yapıp ayrı bir Xib kullanamazsınız
CedricSoubrie

Bir şekilde ayrı NIB dosyası kullanmaktan kaçının ve bunun yerine film şeridini kullanabilirsiniz?
Foriger

@Foriger - Bu noktada değil. Film şeridi tablo görünümlerinde garip bir ihmal, ancak ne olduğu. İsterseniz prototip hücrelerle bu kludgy hack'i kullanın, ama kişisel olarak, sadece üstbilgi / altbilgi görünümleri için NIB'lerin can sıkıcılığına katlandım.
Rob

2
Sadece "yaşlı" olduğunu söylemek yeterince güçlü değil, IMHO. Kabul edilen cevap, üstbilgi ve altbilgi görünümleri için prototip hücrelere sahip olamayacağınız gerçeğini aşmanın zor bir yoludur. Ancak bunun yanlış olduğunu düşünüyorum, çünkü üstbilgilerin ve altbilgilerin ayıklanmasının hücrelerin ayıklanmasıyla aynı şekilde çalıştığını varsayar (üstbilgiler / altbilgiler için daha yeni bir API'nin varlığı durum böyle olmayabilir). Kabul edilen cevap, iOS 5'te anlaşılabilir olan yaratıcı bir çözümdür, ancak iOS 6 ve sonraki sürümlerde, üstbilgi / altbilginin yeniden kullanımı için açıkça tasarlanmış API'yi kullanmak en iyisidir.
Rob

384

Bölüm başlığınız ve / veya altbilginiz olarak bir prototip hücre kullanın.

  • fazladan bir hücre ekleyin ve istediğiniz öğeleri içine koyun.
  • tanımlayıcıyı belirli bir şeye ayarla (benim durumumda SectionHeader)
  • uygulama tableView:viewForHeaderInSection:yöntemi veya tableView:viewForFooterInSection:yöntem
  • [tableView dequeueReusableCellWithIdentifier:]başlığı almak için kullan
  • tableView:heightForHeaderInSection:yöntemi uygular .

(ekran görüntüsüne bakın)

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    static NSString *CellIdentifier = @"SectionHeader"; 
    UITableViewCell *headerView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (headerView == nil){
        [NSException raise:@"headerView == nil.." format:@"No cells with matching CellIdentifier loaded from your storyboard"];
    }
    return headerView;
}  

Düzenleme: Başlık başlığı nasıl değiştirilir (yorumlanmış soru):

  1. Başlık hücresine etiket ekleme
  2. etiketin etiketini belirli bir sayıya ayarlayın (ör. 123)
  3. Senin içinde tableView:viewForHeaderInSection:yöntemle arayarak etiketi almak:
    UILabel *label = (UILabel *)[headerView viewWithTag:123]; 
  1. Artık yeni bir başlık ayarlamak için etiketi kullanabilirsiniz:
    [label setText:@"New Title"];

7
Partiye geç, ancak bölüm başlığı görünümündeki tıklamaları devre dışı bırakmak için, viewForHeaderInSection yöntemde cell.contentView öğesini döndürün (herhangi bir özel UIViews eklemenize gerek yoktur).
paulvs

3
@PaulVon En son projemde bunu yapmaya çalıştım, ancak bunu yaparsanız sadece başlıklarınızdan birine baskı yapmaya çalışın ve çökecek
Hons

13
İOS 6.0'da dequeueReusableHeaderFooterViewWithIdentifiertanıtıldı ve şimdi bu yanıta tercih ediliyor. Ancak bunu doğru kullanmak artık daha fazla adım atıyor. @ Samwize.com/2015/11/06/… kılavuzum var .
samwize

3
SectionHeaderViews'lerinizi UITableViewCells kullanarak oluşturmayın, beklenmedik davranışlar yaratacaktır. Bunun yerine bir UIView kullanın.
AppreciateIt

4
Lütfen hiçbir zaman UITableViewCellbaşlık görünümü olarak kullanmayın . Görsel aksaklıkların hatalarını ayıklamak çok zor olacak - üstbilgi bazen hücrelerin nasıl ayıklandığından dolayı kaybolacak ve saatlerce bakacaksınız ki UITableViewCell, UITableViewbaşlığa ait olmadığını fark edene kadar .
raven_raven

53

İOS 6.0 ve sonraki sürümlerde, yeni dequeueReusableHeaderFooterViewWithIdentifierAPI ile işler değişti .

Ben şöyle özetlenebilir bir rehber (iOS 9 üzerinde test edilmiştir) yazdım :

  1. Alt sınıf UITableViewHeaderFooterView
  2. Alt sınıf görünümü ile Uç oluşturun ve üstbilgi / altbilgideki diğer tüm görünümleri içeren 1 kapsayıcı görünümü ekleyin
  3. Ucu kaydet viewDidLoad
  4. Üstbilgi / altbilgiyi geri almak için uygulayın viewForHeaderInSectionve kullanındequeueReusableHeaderFooterViewWithIdentifier

2
Bir hack DEĞİL olan bu cevap ve bir şeyler yapmak için doğru yol için teşekkür ederiz. Ayrıca, beni UITableViewHeaderFooterVIew ile tanıştırdığınız için teşekkür ederiz. Btw, rehber sadece mükemmel! :)
Roboris

Hoşgeldiniz. Tablo veya koleksiyon görünümü için üstbilgi / altbilgiyi uygulama Apple tarafından çok iyi belgelenmemiştir. Sadece dün storyboad ile tasarım yaparken gösterilen başlıkta sıkışmıştım .
samwize

1
Bu kesinlikle en çok puan alan cevap olmalı!
Ben Williams

Bunu nasıl xib yerine film şeridi üzerinden açıklayabilir misiniz? Bunu yaparken başarılı bir şekilde aldatıyorum, ancak UILabel'lerim boş.
bunkerdive

22

Film şeridinde bir prototip hücre kullanarak iOS7'de çalıştım. Özel bölüm başlığı görünümünde, film şeridinde ayarlanmış bir segri tetikleyen bir düğme var.

Tieme'nin çözümü ile başlayın

Pedro.m'nin belirttiği gibi, bununla ilgili sorun, bölüm başlığına dokunduğunuzda bölümdeki ilk hücrenin seçilmesine neden olmasıdır.

Paul Von'un işaret ettiği gibi, bu, hücrenin tamamı yerine hücrenin contentView değerini döndürerek giderildi.

Ancak, Hons'un işaret ettiği gibi, söz konusu bölüm başlığına uzun bir basış uygulamayı çökertecektir.

Çözüm, herhangi bir motionRecognizers'ı contentView öğesinden kaldırmaktır.

-(UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
     static NSString *CellIdentifier = @"SectionHeader";
     UITableViewCell *sectionHeaderView = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

     while (sectionHeaderView.contentView.gestureRecognizers.count) {
         [sectionHeaderView.contentView removeGestureRecognizer:[sectionHeaderView.contentView.gestureRecognizers objectAtIndex:0]];
     }

     return sectionHeaderView.contentView; }

Bölüm başlığı görünümlerinizde hareketleri kullanmıyorsanız, bu küçük kesmek bunu başarıyor gibi görünüyor.


2
Haklısın. Bunu ve çalışmasını test ettim, bu yüzden araştırmam sırasında bulduğum tüm olası çözümleri içeren yazımı ( hons82.blogspot.it/2014/05/uitableviewheader-done-right.html ) güncelledim . Bu düzeltme için çok teşekkürler
Hons

İyi Cevap ... Teşekkürler Adam
Jogendra.Com

13

Film şeridi kullanıyorsanız, başlık görünümünüzü düzenlemek için tablo görünümünde bir prototip hücre kullanabilirsiniz. Benzersiz bir kimlik ayarlayın ve viewForHeaderInSection, bu kimlikle hücreyi ayıklayabilir ve bir UIView öğesine atabilirsiniz.


12

Bunun bir Hızlı Uygulamasına ihtiyacınız varsa, kabul edilen yanıttaki talimatları izleyin ve ardından UITableViewController'da aşağıdaki yöntemleri uygulayın:

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
    return tableView.dequeueReusableCell(withIdentifier: "CustomHeader")
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

2
Nedense heightForHeaderInSection geçersiz kılınana kadar benim için işe yaramadı.
Entalpi

Bu oldukça eski bir kod. başka bir cevap göndermelisin!
JZ.

Sıkışmıştım çünkü heightForHeaderInSection'ı geçersiz kılmam gerektiğini bilmiyordum. Teşekkürler @Entalpi
guijob

1
@JZ. Swift 3 örneğinizde, aşağıdaki self.tableView.dequeueReusableHeaderFooterView ve Swift 2 ile deque'yi kaldırıyorsunuz: self.tableView.dequeueReusableCellWithIdentifier fark var mı?
Vrutin Rathod

1
Bu kıçımı kurtardı! headerCell'e yükseklik eklenmesi onu görünür hale getirir.
CRey

9

Geldiğim çözüm, film şeridi tanıtımından önce kullanılan çözümle aynı.

Yeni, boş bir arabirim sınıfı dosyası oluşturun. Bir UIView'i tuvale sürükleyin, istediğiniz şekilde düzenleyin.

Ucu elle yükleyin, viewForHeaderInSection veya viewForFooterInSection temsilci yöntemlerinde uygun üstbilgi / altbilgi bölümüne atayın.

Apple'ın bu senaryoyu storyboardlarla basitleştirmesini ve daha iyi veya daha basit bir çözüm aramaya devam etmesini umdum. Örneğin, özel tablo üstbilgileri ve altbilgileri eklenmesi kolaydır.


Film şeridi hücresini başlık yöntemi olarak kullanırsanız iyi bir elma :) stackoverflow.com/questions/9219234/#11396643
Tieme

2
Bunun dışında statik hücreler için değil, yalnızca prototip hücreler için geçerlidir.
Jean-Denis Muys

1
Gerçekten kolay bir çözüm Pixate kullanmaktır ; başlığa referans almadan oldukça fazla özelleştirme yapabilirsiniz. Tek uygulamanız gereken tableView:titleForHeaderInSectiontek şey.
John Starr Dewar

5
Bu gerçek çözüm ... ne yazık ki zirvede değil, bu yüzden onu bulmak biraz zaman aldı. Sahip olduğum sorunları özetledim hons82.blogspot.it/2014/05/uitableviewheader-done-right.html
Hons

Bundan daha kolay, sadece sürükleyip bırakın.
Ricardo

5

Hücrenin contentView öğesini döndürdüğünüzde 2 sorun yaşarsınız:

  1. jestlerle ilgili çökme
  2. contentView'u yeniden kullanmıyorsunuz (her viewForHeaderInSectiongörüşmede yeni hücre oluşturuyorsunuz)

Çözüm:

Tablo üstbilgisi \ altbilgisi için sarmalayıcı sınıfı. UITableViewHeaderFooterViewHücreyi içinde tutan sadece miras alınan konteynerdir.

https://github.com/Magnat12/MGTableViewHeaderWrapperView.git

Sınıfı UITableView'ınıza kaydedin (örneğin, viewDidLoad'da)

- (void)viewDidLoad {
    [super viewDidLoad];
    [self.tableView registerClass:[MGTableViewHeaderWrapperView class] forHeaderFooterViewReuseIdentifier:@"ProfileEditSectionHeader"];
}

UITableViewDelegate'inizde:

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
    MGTableViewHeaderWrapperView *view = [tableView dequeueReusableHeaderFooterViewWithIdentifier:@"ProfileEditSectionHeader"];

    // init your custom cell
    ProfileEditSectionTitleTableCell *cell = (ProfileEditSectionTitleTableCell * ) view.cell;
    if (!cell) {
        cell = [tableView dequeueReusableCellWithIdentifier:@"ProfileEditSectionTitleTableCell"];
        view.cell = cell;
    }

    // Do something with your cell

    return view;
}

2

Tembel olarak üstbilgi / altbilgi görünümleri oluşturmak için aşağıdakileri yapardım:

  • Film şeridine bölüm üstbilgisi / altbilgisi için serbest biçimli görünüm denetleyicisi ekleme
  • Görünüm denetleyicisindeki başlık için her şeyi işleme
  • Tablo görünümü denetleyicisinde yeniden doldurulmuş bölüm üstbilgileri / altbilgileri için değiştirilebilir bir görünüm denetleyicileri dizisi sağlayın [NSNull null]
  • ViewForHeaderInSection / viewForFooterInSection öğesinde, görünüm denetleyicisi henüz yoksa, instantiateViewControllerWithIdentifier hikaye tahtalarıyla oluşturun, dizide hatırlayın ve görünüm denetleyicileri görünümüne dönün

2

Header'ın tüm uygun adımları gerçekleştirse bile asla yeniden kullanılmadığı bir senaryo içinde sorun yaşadım.

Boş bölümleri (0 satır) gösterme durumunu elde etmek isteyen herkese bir ipucu notu olarak uyarınız:

dequeueReusableHeaderFooterViewWithIdentifier, en az bir satır dönene kadar üstbilgiyi yeniden kullanmayacak

Umarım yardımcı olur


1

Damon'un önerisini takip etmek , açıklama göstergesine sahip normal bir satır gibi başlığı nasıl seçilebilir hale getirdim.

Başlığın prototip hücresine UIButton (alt sınıf adı "ButtonWithArgument") alt sınıfı bir Düğme ekledim ve başlık metnini sildim (kalın "Başlık" metni prototip hücresindeki başka bir UILabel)

Arayüz Oluşturucudaki Düğme

daha sonra Düğmeyi tüm başlık görünümüne ayarlayın ve Avario'nun hilesi ile bir açıklama göstergesi ekledi

- (UIView *)tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section
{
    static NSString *CellIdentifier = @"PersonGroupHeader";
    UITableViewCell *headerView = (UITableViewCell *) [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if(headerView == nil)
    {
        [NSException raise:@"headerView == nil, PersonGroupTableViewController" format:[NSString stringWithFormat:@"Storyboard does not have prototype cell with identifier %@",CellIdentifier]];
    }

    //  https://stackoverflow.com/a/24044628/3075839
    while(headerView.contentView.gestureRecognizers.count)
    {
        [headerView.contentView removeGestureRecognizer:[headerView.contentView.gestureRecognizers objectAtIndex:0]];
    }


    ButtonWithArgument *button = (ButtonWithArgument *)[headerView viewWithTag:4];
    button.frame = headerView.bounds; // set tap area to entire header view
    button.argument = [[NSNumber alloc] initWithInteger:section]; // from ButtonWithArguments subclass
    [button addTarget:self action:@selector(headerViewTap:) forControlEvents:UIControlEventTouchUpInside];

    // https://stackoverflow.com/a/20821178/3075839
    UITableViewCell *disclosure = [[UITableViewCell alloc] init];
    disclosure.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
    disclosure.userInteractionEnabled = NO;
    disclosure.frame = CGRectMake(button.bounds.origin.x + button.bounds.size.width - 20 - 5, // disclosure 20 px wide, right margin 5 px
          (button.bounds.size.height - 20) / 2,
          20,
          20);
    [button addSubview:disclosure];

    // configure header title text

    return headerView.contentView;
}

- (CGFloat)tableView:(UITableView *)tableView heightForHeaderInSection:(NSInteger)section
{
    return 35.0f;
}

-(void) headerViewTap:(UIGestureRecognizer *)gestureRecognizer;
{
    NSLog(@"header tap");
    NSInteger section = ((NSNumber *)sender.argument).integerValue;
    // do something here
}

ButtonWithArgument.h

#import <UIKit/UIKit.h>

@interface ButtonWithArgument : UIButton
@property (nonatomic, strong) NSObject *argument;
@end

ButtonWithArgument.m

#import "ButtonWithArgument.h"
@implementation ButtonWithArgument
@end

1

Tieme'nin çözümünü bir üs olarak kullanmalısınız, ancakviewWithTag: ve diğer balık yaklaşımlarını , bunun yerine başlığınızı yeniden yüklemeyi deneyin (bu bölümü yeniden ).

Bu nedenle, özel hücre başlığı görünümünüzü tüm süslü AutoLayoutşeylerle oturttuktan sonra, aykırıp içeriğe geri dönün.

-(UIView *) tableView:(UITableView *)tableView viewForHeaderInSection:(NSInteger)section {
 static NSString *CellIdentifier = @"SectionHeader"; 

    SettingsTableViewCell *sectionHeaderCell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];

    sectionHeaderCell.myPrettyLabel.text = @"Greetings";
    sectionHeaderCell.contentView.backgroundColor = [UIColor whiteColor]; // don't leave this transparent

    return sectionHeaderCell.contentView;
}  

1

Başlığın bir görünüm dizisine dayandığı bir çözüm ne olacak?

class myViewController: UIViewController {
    var header: [UILabel] = myStringArray.map { (thisTitle: String) -> UILabel in
        let headerView = UILabel()
            headerView.text = thisTitle
    return(headerView)
}

Delege içerisinde bir sonraki:

extension myViewController: UITableViewDelegate {
    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        return(header[section])
    }
}

1
  1. Hücre ekle StoryBoardve ayarlareuseidentified

    sb

  2. kod

    class TP_TaskViewTableViewSectionHeader: UITableViewCell{
    }
    

    ve

    bağlantı

  3. kullanın:

    func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let header = tableView.dequeueReusableCell(withIdentifier: "header", for: IndexPath.init(row: 0, section: section))
        return header
    }
    

2
Üstbilgi eklemek için doğru yol bu değil
Manish Malviya

2
o zaman yol nedir?
Pramod Shukla

1

Laszlo cevabına benzer ancak aynı prototip hücresini hem tablo hücreleri hem de bölüm başlığı hücresi için tekrar kullanabilirsiniz. Aşağıdaki ilk iki işlevi UIViewController alt sınıfınıza ekleyin

override func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell") as! DataCell
    cell.data1Label.text = "DATA KEY"
    cell.data2Label.text = "DATA VALUE"
    return cell
}

override func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
    return 75
}

// Example of regular data cell dataDelegate to round out the example
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "DataCell", for: indexPath) as! PlayerCell

    cell.data1Label.text = "\(dataList[indexPath.row].key)"
    cell.data2Label.text = "\(dataList[indexPath.row].value)"
    return cell
}

0

İşte Swift'te @Vitaliy Gozhenko'nun cevabı .
Özetlemek gerekirse, bir UITableViewCell içeren bir UITableViewHeaderFooterView oluşturacaksınız. Bu UITableViewCell "dequeuable" olacaktır ve film şeridinde tasarlayabilirsiniz.

  1. UITableViewHeaderFooterView sınıfı oluşturma

    class CustomHeaderFooterView: UITableViewHeaderFooterView {
    var cell : UITableViewCell? {
        willSet {
            cell?.removeFromSuperview()
        }
        didSet {
            if let cell = cell {
                cell.frame = self.bounds
                cell.autoresizingMask = [UIViewAutoresizing.FlexibleHeight, UIViewAutoresizing.FlexibleWidth]
                self.contentView.backgroundColor = UIColor .clearColor()
                self.contentView .addSubview(cell)
            }
        }
    }
    
  2. ViewDidLoad işlevinizde tablo görünümünüzü bu sınıfa takın:

    self.tableView.registerClass(CustomHeaderFooterView.self, forHeaderFooterViewReuseIdentifier: "SECTION_ID")
    
  3. Bir bölüm üstbilgisi için sorulduğunda, CustomHeaderFooterView işlevini ayıklayın ve içine bir hücre ekleyin

    func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
        let view = self.tableView.dequeueReusableHeaderFooterViewWithIdentifier("SECTION_ID") as! CustomHeaderFooterView
        if view.cell == nil {
            let cell = self.tableView.dequeueReusableCellWithIdentifier("Cell")
            view.cell = cell;
        }
    
        // Fill the cell with data here
    
        return view;
    }
    
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.