Bir film şeridinde, birden fazla denetleyiciyle kullanmak için nasıl özel bir hücre oluşturabilirim?


216

Üzerinde çalıştığım bir uygulamada film şeridi kullanmaya çalışıyorum. Uygulamada Listeler ve Kullanıcılar vardır ve her biri diğerinin (bir listenin üyeleri, bir kullanıcının sahip olduğu listeler) bir koleksiyon içerir. Buna göre derslerim ListCellve UserCellderslerim var. Amaç, bunların uygulama boyunca yeniden kullanılabilir olmasını sağlamaktır (yani, tablo görünümü denetleyicilerimden herhangi birinde).

Burada bir sorunla karşılaşıyorum.

Film şeridinde herhangi bir görünüm denetleyicisinde yeniden kullanılabilen özel bir tablo görünümü hücresini nasıl oluştururum?

İşte şimdiye kadar denediğim özel şeyler.

  • Denetleyici # 1'de bir prototip hücre ekledi, sınıfı UITableViewCellalt sınıfıma ayarladı, yeniden kullanma kimliğini ayarladı, etiketleri ekledi ve sınıfın çıkışlarına bağladı. Denetleyici # 2'de boş bir prototip hücresi ekledi, onu aynı sınıfa ayarlayın ve kimliği önceki gibi yeniden kullanın. Çalıştığında, hücreler Denetleyici # 2'de gösterildiğinde etiketler asla görünmez. Denetleyici # 1'de iyi çalışıyor.

  • Her hücre tipini farklı bir NIB'de tasarladı ve uygun hücre sınıfına kadar kabloladı. Film şeridinde boş bir prototip hücre ekledi ve sınıfını ayarlayın ve kimliğimi hücre sınıfımla ilgili olacak şekilde yeniden kullanın. Denetleyicilerin viewDidLoadyöntemlerinde, bu NIB dosyalarını yeniden kullanım kimliği için kaydettirin. Gösterildiğinde, her iki denetleyicideki hücreler prototip gibi boştu.

  • Her iki denetleyicideki prototipler boş bırakılır ve sınıf ayarlanır ve id, hücre sınıfım için yeniden kullanılır. Hücrelerin kullanıcı arayüzünü tamamen kodla yapılandırdı. Hücreler tüm kontrolörlerde mükemmel çalışır.

İkinci durumda, prototipin her zaman NIB'yi geçersiz kıldığından şüpheleniyorum ve prototip hücrelerini öldürdüysem, NIB'imi yeniden kullanma kimliği için kaydettirmek işe yarardı. Ama sonra hücrelerden diğer karelere selamlar ayarlayamazdım, bu da film şeridi kullanmanın tüm noktası.

Günün sonunda, iki şey istiyorum: film şeridinde tablo görünümü tabanlı akışları bağlayın ve koddan ziyade hücre düzenlerini görsel olarak tanımlayın. Bunların ikisine de nasıl ulaşacağımı göremiyorum.

Yanıtlar:


205

Anladığım kadarıyla:

  1. Birden çok film şeridi sahnesinde kullanılabilen IB'de bir hücre tasarlayın.
  2. Hücrenin bulunduğu sahneye bağlı olarak, o hücreden benzersiz film şeridi sekmelerini yapılandırın.

Ne yazık ki, şu anda bunu yapmanın bir yolu yok. Önceki girişimlerinizin neden işe yaramadığını anlamak için film şeridi ve prototip tablo görünümü hücrelerinin nasıl çalıştığı hakkında daha fazla bilgi sahibi olmanız gerekir. ( Bu diğer girişimlerin neden işe yaramadığı konusunda umursamıyorsanız , şimdi ayrılmaktan çekinmeyin. Sizin için hiçbir hata dosyası önermekten başka sihirli bir çözümüm yok.)

Bir film şeridi, özünde, .xib dosyalarının bir koleksiyonundan çok daha fazlası değildir. Film şeridinden bazı prototip hücreleri olan bir tablo görünümü denetleyicisi yüklediğinizde, şunlar olur:

  • Her prototip hücresi aslında kendi gömülü mini ucudur. Böylece tablo görünümü denetleyicisi yüklenirken, prototip hücresinin uçlarının ve çağrılarının her biri boyunca çalışır -[UITableView registerNib:forCellReuseIdentifier:].
  • Tablo görünümü denetleyiciden hücreleri ister.
  • Muhtemelen araıyorsun -[UITableView dequeueReusableCellWithIdentifier:]
  • Belirli bir yeniden kullanım tanımlayıcısına sahip bir hücre talep ettiğinizde, bir ucunun kayıtlı olup olmadığını kontrol eder. Varsa, o hücrenin bir örneğini başlatır. Bu, aşağıdaki adımlardan oluşur:

    1. Hücrenin ucunda tanımlandığı gibi hücre sınıfına bakın. Arayın [[CellClass alloc] initWithCoder:].
    2. -initWithCoder:Yöntem geçer ve kalem ucundaki tanımlandı subviews ve setler özelliklerini ekler. ( IBOutletmuhtemelen test etmeme rağmen buraya da bağlandım; olabilir -awakeFromNib)
  • Hücrenizi istediğiniz gibi yapılandırırsınız.

Burada dikkat edilmesi gereken önemli şey , hücrenin sınıfı ile hücrenin görsel görünümü arasında bir ayrım olmasıdır . Aynı sınıftan iki ayrı prototip hücre oluşturabilirsiniz, ancak alt görünümleri tamamen farklı şekilde düzenlenmiştir. Aslında, varsayılan UITableViewCellstilleri kullanırsanız, olan tam olarak budur. Örneğin, "Varsayılan" stil ve "Altyazı" stilinin ikisi de aynı UITableViewCellsınıf tarafından temsil edilir .

Bu önemlidir : Hücre sınıfının belirli bir görünüm hiyerarşisi ile bire bir ilişkisi yoktur . Görünüm hiyerarşisi, tamamen bu özel denetleyiciye kaydedilen prototip hücresindekiler tarafından belirlenir.

Ayrıca, hücrenin yeniden kullanım tanımlayıcısının bazı küresel hücre dispanserinde kayıtlı olmadığını unutmayın. Yeniden kullanım tanımlayıcısı yalnızca tek bir UITableViewörnek bağlamında kullanılır .


Bu bilgi göz önüne alındığında, yukarıdaki girişimlerinizde neler olduğuna bakalım.

Denetleyici # 1'de, bir prototip hücre ekledim, sınıfı UITableViewCell alt sınıfıma ayarladım, yeniden kullanım kimliğini ayarlayın, etiketleri ekledik ve sınıfın çıkışlarına bağladım. Denetleyici # 2'de boş bir prototip hücresi ekledi, onu aynı sınıfa ayarlayın ve kimliği önceki gibi yeniden kullanın. Çalıştığında, hücreler Denetleyici # 2'de gösterildiğinde etiketler asla görünmez. Denetleyici # 1'de iyi çalışıyor.

Bu bekleniyor. Her iki hücre de aynı sınıfa sahip olsa da, Denetleyici # 2'deki hücreye aktarılan görünüm hiyerarşisi tamamen alt görünümlerden yoksundu. Yani boş bir hücreniz var, bu da tam olarak prototipe koyduğunuz şey.

Her hücre tipini farklı bir NIB'de tasarladı ve uygun hücre sınıfına kadar kabloladı. Film şeridinde boş bir prototip hücre ekledi ve sınıfını ayarlayın ve kimliğimi hücre sınıfımla ilgili olarak yeniden kullanın. Denetleyicilerin viewDidLoad yöntemlerinde, yeniden kullanım kimliği için bu NIB dosyalarını kaydettirin. Gösterildiğinde, her iki denetleyicideki hücreler prototip gibi boştu.

Yine, bu bekleniyor. Yeniden kullanım tanımlayıcısı film şeridi sahneleri veya uçları arasında paylaşılmaz, bu nedenle bu farklı hücrelerin hepsinin aynı yeniden kullanım tanımlayıcısına sahip olması anlamsızdı. Tablo görünümünden geri aldığınız hücre, film şeridinin o sahnesindeki prototip hücreyle eşleşen bir görünüme sahip olacaktır.

Ancak bu çözüm yakındı. Belirttiğiniz gibi, sadece hücre içeren hücreyi -[UITableView registerNib:forCellReuseIdentifier:]geçirerek programlı olarak arayabilir UINibve aynı hücreyi geri alabilirsiniz. (Bu, prototipin ucu "geçersiz kıldığı" için değildir; sadece ucu masa görüntüsüne kaydettirmediniz, bu yüzden hala film şeridine gömülmüş uca bakıyordu.) Ne yazık ki, bu yaklaşımda bir kusur var - film şeridi segu'lerini bağımsız bir uçtaki bir hücreye bağlamanın bir yolu yoktur.

Her iki denetleyicideki prototipler boş bırakılır ve sınıf ayarlanır ve id, hücre sınıfım için yeniden kullanılır. Hücrelerin kullanıcı arayüzünü tamamen kodla yapılandırdı. Hücreler tüm kontrolörlerde mükemmel çalışır.

Doğal olarak. Umarım, bu şaşırtıcı değildir.


Bu yüzden işe yaramadı. Hücrelerinizi bağımsız uçlarda tasarlayabilir ve birden fazla storyboard sahnesinde kullanabilirsiniz; şu anda storyboard segu'lerini bu hücrelere bağlayamazsınız. Umarım, bunu okuma sürecinde bir şeyler öğrendiniz.


Ah, anladım. Yanlış anlaşılmamı çivirdin - görünüm hiyerarşisi sınıfımdan tamamen bağımsız. Geçmişe bakıldığında bariz! Harika cevap için teşekkürler.
Cliff W

Artık imkansız değil gibi görünüyor: stackoverflow.com/questions/8574188/…
Rich Apodaca

7
@RichApodaca Cevabımda bu çözümden bahsettim. Ancak film şeridinde değil; ayrı bir uç var. Yani segue'leri bağlayamazsınız veya başka storyboard-ish şeyleri yapamazsınız. Bu nedenle, ilk soruyu tamamen ele almamaktadır.
BJ Homer

XCode8'den itibaren, yalnızca bir storyboard çözümü istiyorsanız aşağıdaki geçici çözüm işe yarıyor gibi görünüyor. Adım 1) Prototip hücrenizi ViewController # 1'de bir tablo görünümünde oluşturun ve özel UITableViewCell sınıfıyla ilişkilendirin Adım 2) Bu hücreyi ViewController # 2'nin tablo görünümünde kopyalayın / yapıştırın. Zaman içinde film şeridinde yaptığınız kopyaları silerek ve güncellenmiş prototipe yapıştırarak güncellemeleri hücrenin kopyalarına yaymayı hatırlamanız gerekir.
jengelsma

Harika bir cevap, yine de takip eden bir sorum var:> "Bir storyboard, özünde, .xib dosyalarının bir koleksiyonundan çok daha fazla değildir" Bu durumda, bir xib'i gömmek neden bu kadar zor? bir film şeridi mi?
willcwf

58

BJ Homer'in büyük cevabına rağmen bir çözüm bulduğumu hissediyorum. Testlerim ilerledikçe işe yarıyor.

Kavram: Xib hücresi için özel bir sınıf oluşturun. Orada bir dokunma olayı için bekleyebilir ve segue'i programlı olarak gerçekleştirebilirsiniz. Şimdi tek ihtiyacımız olan Segue'i yapan kontrolöre bir referans. Benim çözümüm onu ​​yerleştirmek tableView:cellForRowAtIndexPath:.

Misal

DetailedTaskCell.xibBirden çok tablo görünümlerinde kullanmak istediğiniz bir tablo hücresi içeren bir var :

DetailedTaskCell.xib

TaskGuessTableCellBu hücre için özel bir sınıf var:

resim açıklamasını buraya girin

Sihir yapılan yer burasıdır.

// TaskGuessTableCell.h
#import <Foundation/Foundation.h>

@interface TaskGuessTableCell : UITableViewCell
@property (nonatomic, weak) UIViewController *controller;
@end

// TashGuessTableCell.m
#import "TaskGuessTableCell.h"

@implementation TaskGuessTableCell

@synthesize controller;

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
    NSIndexPath *path = [controller.tableView indexPathForCell:self];
    [controller.tableView selectRowAtIndexPath:path animated:NO scrollPosition:UITableViewScrollPositionNone];
    [controller performSegueWithIdentifier:@"FinishedTask" sender:controller];
    [super touchesEnded:touches withEvent:event];
}

@end

Birden segues var ama hepsi aynı isme sahip: "FinishedTask". Burada esnek olmanız gerekiyorsa, başka bir mülk eklemenizi öneririm.

ViewController şöyle görünür:

// LogbookViewController.m
#import "LogbookViewController.h"
#import "TaskGuessTableCell.h"

@implementation LogbookViewController

- (void)viewDidLoad
{
    [super viewDidLoad]

    // register custom nib
    [self.tableView registerNib:[UINib nibWithNibName:@"DetailedTaskCell" bundle:[NSBundle mainBundle]] forCellReuseIdentifier:@"DetailedTaskCell"];
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    TaskGuessTableCell *cell;

    cell = [tableView dequeueReusableCellWithIdentifier:@"DetailedTaskCell"];
    cell.controller = self; // <-- the line that matters
    // if you added the seque property to the cell class, set that one here
    // cell.segue = @"TheSegueYouNeedToTrigger";
    cell.taskTitle.text  = [entry title];
    // set other outlet values etc. ...

    return cell;
}

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if([[segue identifier] isEqualToString:@"FinishedTask"])
    {
        // do what you have to do, as usual
    }

}

@end

Aynı şeyi elde etmek için daha zarif yollar olabilir ama - işe yarıyor! :)


1
Teşekkürler, projemde bu yaklaşımı uyguluyorum. Bunun yerine indexPath'i almak ve satırı kendiniz seçmek zorunda kalmamak için bu yöntemi geçersiz kılabilirsiniz: - (void) setSelected: (BOOL) canlandırılmış animasyonlu: (BOOL) canlandırılmış {[süper setSelected: canlandırılmış animasyonlu: canlandırılmış]; if (seçili) [self.controller performSegueWithIdentifier: self.segue sender: self]; } Ben süper [super touchesEnded: dokunurEvent: olay]; çağrılırken hücreyi seçeceğini düşündüm. Orada değilse ne zaman seçildiğini biliyor musunuz?
thejaz

9
Bu çözümle, hücrenin içine her dokunuşta segue'i tetiklediğinizi unutmayın. Bu, hücreyi kaydırmaya çalışıyorsanız, aslında onu seçmeye çalışmamanızı içerir. -setSelected:Hücreyi geçersiz kılma ve segue'i yalnızca - ' NOden geçişte tetikleme şansınız daha iyi olabilir YES.
BJ Homer

Şansım olsa iyi olur setSelected:BJ. Teşekkürler. Gerçekten de, bu yanlış bir çözüm ( yanlış geliyor ), ama aynı zamanda işe yarıyor, bu yüzden bu düzeltilene kadar kullanıyorum (veya Apple'ın mahkemesinde bir şey değişiyor).
Ben Kreeger

16

Bunu arıyordum ve bu cevabı Richard Venable'dan buldum . Benim için çalışıyor.

iOS 5, UITableView'da yeni bir yöntem içerir: registerNib: forCellReuseIdentifier:

Kullanmak için bir uca UITableViewCell koyun. Uçtaki tek kök nesne olmalıdır.

Tablonuzu yükledikten sonra ucu kaydedebilirsiniz, daha sonra dequeueReusableCellWithIdentifier öğesini çağırdığınızda: hücre tanımlayıcıyla, tıpkı bir Storyboard prototip hücresi kullanmış gibi, uçtan çekecektir.


10

BJ Homer neler olup bittiğine dair mükemmel bir açıklama yaptı.

Pratik bir bakış açısından, xibs ve connect segues olarak hücreleriniz olmadığı göz önüne alındığında, seçilecek en iyisi hücreyi bir xib olarak sahip olmaktır - geçişlerin bakımı, birden fazla yerdeki hücre düzenlerinden ve özelliklerden çok daha kolaydır ve segue'lerinizin farklı kontrolörlerinizden farklı olması muhtemeldir. Segue'i doğrudan tablo görünümü denetleyicinizden bir sonraki denetleyiciye tanımlayabilir ve kodda gerçekleştirebilirsiniz. .

Başka bir not, hücrenizin ayrı bir xib dosyası olarak sahip olmanızın herhangi bir eylemi vb. Doğrudan tablo görünümü denetleyicisine bağlayabilmenizi engellemesidir (bunu zaten yapmadım - dosyanın sahibini anlamlı bir şey olarak tanımlayamazsınız. ). CellForRowAtIndexPath bir hücrenin tablo görünümü denetleyicisine uygun olması ve denetleyiciyi zayıf bir özellik olarak eklemek için bir protokol tanımlayarak bu soruna geçici olarak çalışıyorum.


10

Hızlı 3

BJ Homer mükemmel bir açıklama yaptı, konsepti anlamama yardımcı oluyor. Bunun için make a custom cell reusable in storyboardherhangi bir TableViewController içinde kullanılabilir mix the Storyboard and xibyaklaşım gerekir. Biz adında bir hücreyi olduğunu varsayalım CustomCellkullanılacak olan TableViewControllerOneve TableViewControllerTwo. Adım adım yapıyorum.
1. Dosya> Yeni> Dosya> Kakao Dokunmatik Sınıfını Seç> İleri> Sınıfınızın Adını Ver (örneğin CustomCell)> UITableVieCell olarak Alt Sınıfı seçin> Ayrıca XIB dosyası oluştur onay kutusunu işaretleyin ve İleri'ye basın.
2. Hücreyi istediğiniz gibi özelleştirin ve hücre için nitelik denetçisinde tanımlayıcıyı ayarlayın, burada olarak ayarlayacağız CellIdentifier. Bu tanımlayıcı, Hücreyi tanımlamak ve yeniden kullanmak için ViewController'ınızda kullanılacaktır.
3. Şimdi sadeceregister this cellbizim ViewController4.viewDidLoad. Herhangi bir başlatma yöntemine gerek yoktur.
Şimdi bu özel hücreyi herhangi bir tableView içinde kullanabiliriz.

TableViewControllerOne içinde

let reuseIdentifier = "CellIdentifier"

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UINib(nibName: "CustomCell", bundle: nil), forCellReuseIdentifier: reuseIdentifier)
} 

func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier:reuseIdentifier, for: indexPath) as! CustomCell
    return cell!
}

5

Segues için test edilmemiş, aynı VC için hücre yüklemek için bir yol buldum. Bu, hücreyi ayrı bir uçta oluşturmak için bir geçici çözüm olabilir

Bir VC ve 2 tablonuz olduğunu ve film şeridinde bir hücre tasarlamak ve her iki tabloda kullanmak istediğinizi varsayalım.

(örn: bir tablo ve sonuçlar için bir tablo içeren bir UISearchController içeren bir arama alanı ve her ikisinde de aynı Hücreyi kullanmak istiyorsunuz)

Denetleyici hücre istediğinde bunu yapın:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString * identifier = @"CELL_ID";

    ContactsCell *cell = [self.YOURTABLEVIEW dequeueReusableCellWithIdentifier:identifier];
  // Ignore the "tableView" argument
}

Ve burada hücreniz film şeridinden var


Bunu denedim ve hücrelerin asla yeniden kullanılmadığı halde çalışıyor gibi görünüyor. Sistem her zaman yeni hücreler oluşturur ve dağıtır.
GilroyKilroy

Bu, Storyboard'larla Tablo Görünümüne Arama Çubuğu Ekleme bölümünde bulunan öneriyle aynıdır . Eğer ilgileniyorsanız, orada bu çözümün daha ayrıntılı bir açıklaması var (arayın tableView:cellForRowAtIndexPath:).
Temmuz'da

ama bu daha az metin ve soruyu cevaplıyor
João Nunes
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.