ios - länge - title tag wordpress




Wie laden Sie benutzerdefinierte UITableViewCells aus Xib-Dateien? (14)

Registrieren

Nach iOS 7 wurde dieser Prozess auf ( swift 3.0 ) vereinfacht:

// For registering nib files
tableView.register(UINib(nibName: "MyCell", bundle: Bundle.main), forCellReuseIdentifier: "cell")

// For registering classes
tableView.register(MyCellClass.self, forCellReuseIdentifier: "cell")

( Hinweis ) Dies ist auch möglich, indem Sie die Zellen in den .xib oder .stroyboard Dateien als Prototypzellen erstellen. Wenn Sie eine Klasse anhängen müssen, können Sie den UITableViewCell auswählen und die entsprechende Klasse hinzufügen (muss natürlich ein Nachkomme von UITableViewCell ).

Auspacken

Und später mit ( swift 3.0 ) aus der Warteschlange genommen:

override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
    let cell : UITableViewCell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)

    cell.textLabel?.text = "Hello"

    return cell
}

Der Unterschied ist, dass diese neue Methode nicht nur die Zelle aus der Warteschlange nimmt, sondern auch, wenn sie nicht existiert (das bedeutet, dass Sie keine if (cell == nil) Shenanigans machen müssen) und die Zelle bereit ist, genau so zu verwenden im obigen Beispiel.

( Warnung ) tableView.dequeueReusableCell(withIdentifier:for:) hat das neue Verhalten, wenn Sie das andere (ohne indexPath: tableView.dequeueReusableCell(withIdentifier:for:) aufrufen, erhalten Sie das alte Verhalten, in dem Sie nach nil UITableViewCell? und es selbst instanziieren müssen, beachten Sie die UITableViewCell? Rückgabewert.

if let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? MyCellClass
{
    // Cell be casted properly
    cell.myCustomProperty = true
}
else
{
    // Wrong type? Wrong identifier?
}

Und natürlich ist der Typ der zugehörigen Klasse der Zelle der, den Sie in der .xib-Datei für die UITableViewCell Unterklasse definiert haben, oder alternativ die andere Registermethode.

Aufbau

Idealerweise wurden Ihre Zellen bereits hinsichtlich der Darstellung und der Positionierung der Inhalte (wie Beschriftungen und cellForRowAtIndexPath ) bereits bei der Registrierung cellForRowAtIndexPath , und bei der Methode cellForRowAtIndexPath füllen Sie sie einfach aus.

Alle zusammen

class MyCell : UITableViewCell
{
    // Can be either created manually, or loaded from a nib with prototypes
    @IBOutlet weak var labelSomething : UILabel? = nil
}

class MasterViewController: UITableViewController 
{
    var data = ["Hello", "World", "Kinda", "Cliche", "Though"]

    // Register
    override func viewDidLoad()
    {
        super.viewDidLoad()

        tableView.register(MyCell.self, forCellReuseIdentifier: "mycell")
        // or the nib alternative
    }

    override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
    {
        return data.count
    }

    // Dequeue
    override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
    {
        let cell = tableView.dequeueReusableCell(withIdentifier: "mycell", for: indexPath) as! MyCell

        cell.labelSomething?.text = data[indexPath.row]

        return cell
    }
}

Und natürlich ist dies alles in ObjC mit den gleichen Namen verfügbar.

Die Frage ist einfach: Wie laden Sie benutzerdefinierte UITableViewCell aus Xib-Dateien? Auf diese Weise können Sie mit dem Interface Builder Ihre Zellen entwerfen. Die Antwort ist anscheinend nicht einfach aufgrund von Speicherverwaltungsproblemen. Dieser Thread erwähnt das Problem und schlägt eine Lösung vor, ist aber vor NDA-Release und Code fehlt. Hier ist ein langer Thread , der das Problem behandelt, ohne eine definitive Antwort zu geben.

Hier ist ein Code, den ich benutzt habe:

static NSString *CellIdentifier = @"MyCellIdentifier";

MyCell *cell = (MyCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:CellIdentifier owner:self options:nil];
    cell = (MyCell *)[nib objectAtIndex:0];
}

Um diesen Code zu verwenden, erstellen Sie MyCell.m / .h, eine neue Unterklasse von UITableViewCell und fügen Sie IBOutlets für die IBOutlets Komponenten hinzu. Erstellen Sie dann eine neue "Leere XIB" -Datei. Öffnen Sie die Xib-Datei in IB, fügen Sie ein UITableViewCell Objekt hinzu, legen Sie den Bezeichner auf "MyCellIdentifier" fest und legen Sie seine Klasse auf MyCell fest, und fügen Sie Ihre Komponenten hinzu. Abschließend verbinden Sie die IBOutlets mit den Komponenten. Beachten Sie, dass wir den Besitzer der Datei nicht in IB festgelegt haben.

Andere Methoden empfehlen, den Eigentümer der Datei festzulegen und vor Speicherlecks zu warnen, wenn der Xib nicht über eine zusätzliche Factory-Klasse geladen wird. Ich habe das oben unter Instrumente / Lecks getestet und sah keine Speicherlecks.

Was ist der übliche Weg, um Zellen von Xibs zu laden? Setzen wir den Eigentümer der Datei? Brauchen wir eine Fabrik? Wenn ja, wie sieht der Code für die Fabrik aus? Wenn es mehrere Lösungen gibt, lassen Sie uns die Vor- und Nachteile jedes einzelnen klären ...


  1. Erstellen Sie Ihre eigene benutzerdefinierte Klasse AbcViewCell Unterklasse von UITableViewCell ( UITableViewCell Sie sicher, dass der Klassendateiname und der Name der Nib-Datei identisch sind)

  2. Erstellen Sie diese Erweiterungsklassenmethode.

    extension UITableViewCell {
        class func fromNib<T : UITableViewCell>() -> T {
            return Bundle.main.loadNibNamed(String(describing: T.self), owner: nil, options: nil)?[0] as! T
        }
    }
    
  3. Benutze es.

    let cell: AbcViewCell = UITableViewCell.fromNib()


Das Laden von UITableViewCells von XIBs spart eine Menge Code, führt aber normalerweise zu einer schrecklichen Scrollgeschwindigkeit (tatsächlich ist es nicht die XIB, sondern die übermäßige Verwendung von UIViews, die dies verursacht).

Ich schlage vor, Sie schauen sich das an: Verweis Verweis


Das Neuladen der NIB ist teuer. Besser, es einmal zu laden, dann instanziieren Sie die Objekte, wenn Sie eine Zelle benötigen. Beachten Sie, dass Sie mit dieser Methode UIImageViews etc. zur Nib hinzufügen können, sogar mehrere Zellen. (Apples registerNIB iOS5 erlaubt nur ein Objekt der obersten Ebene - Bug 10580062 "iOS5 tableView registerNib: übermäßig restriktiv"

Also mein Code ist unten - du liest in der NIB einmal (in Initialisieren wie ich oder in ViewDidload - was auch immer. Von da an instanzierst du die Spitze in Objekte und wählst dann die, die du brauchst. Das ist viel effizienter als das Laden der Spitze über und über.

static UINib *cellNib;

+ (void)initialize
{
    if(self == [ImageManager class]) {
        cellNib = [UINib nibWithNibName:@"ImageManagerCell" bundle:nil];
        assert(cellNib);
    }
}

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellID];
    if(cell == nil) {
        NSArray *topLevelItems = [cellNib instantiateWithOwner:nil options:nil];
        NSUInteger idx = [topLevelItems indexOfObjectPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop)
                            {
                                UITableViewCell *cell = (UITableViewCell *)obj;
                                return [cell isKindOfClass:[UITableViewCell class]] && [cell.reuseIdentifier isEqualToString:cellID];
                            } ];
        assert(idx != NSNotFound);
        cell = [topLevelItems objectAtIndex:idx];
    }
    cell.textLabel.text = [NSString stringWithFormat:@"Howdie %d", indexPath.row];

    return cell;
}

Die richtige Vorgehensweise besteht darin, eine UITableViewCell-Unterklassenimplementierung, einen Header und eine XIB zu erstellen. Entfernen Sie in der XIB alle Ansichten und fügen Sie einfach eine Tabellenzelle hinzu. Legen Sie die Klasse als den Namen der UITableViewCell-Unterklasse fest. Legen Sie für den Dateibesitzer den Klassennamen der UITableViewController-Klasse fest. Verbinden Sie den Dateibesitzer mit der Zelle tableViewCell mit der Zelle.

In der Header-Datei:

UITableViewCell *_tableViewCell;
@property (assign) IBOutlet UITableViewCell *tableViewCell;

In der Implementierungsdatei:

@synthesize tableViewCell = _tableViewCell;

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

    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:kCellIdentifier];
    if (cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:kCellIdentifier owner:self options:nil];
        cell = _tableViewCell;
        self.tableViewCell = nil;
    }

    return cell;
}

Diese Erweiterung benötigt Xcode7 beta6

extension NSBundle {
    enum LoadViewError: ErrorType {
        case ExpectedXibToExistButGotNil
        case ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        case XibReturnedWrongType
    }

    func loadView<T>(name: String) throws -> T {
        let topLevelObjects: [AnyObject]! = loadNibNamed(name, owner: self, options: nil)
        if topLevelObjects == nil {
            throw LoadViewError.ExpectedXibToExistButGotNil
        }
        if topLevelObjects.count != 1 {
            throw LoadViewError.ExpectedXibToContainJustOneButGotDifferentNumberOfObjects
        }
        let firstObject: AnyObject! = topLevelObjects.first
        guard let result = firstObject as? T else {
            throw LoadViewError.XibReturnedWrongType
        }
        return result
    }
}

Erstellen Sie eine Xib-Datei, die nur 1 benutzerdefinierte UITableViewCell enthält.

Lade es.

let cell: BacteriaCell = try NSBundle.mainBundle().loadView("BacteriaCell")

Hier ist ein universeller Ansatz zum Registrieren von Zellen in UITableView :

protocol Reusable {
    static var reuseID: String { get }
}

extension Reusable {
    static var reuseID: String {
        return String(describing: self)
    }
}

extension UITableViewCell: Reusable { }

extension UITableView {

func register<T: UITableViewCell>(cellClass: T.Type = T.self) {
    let bundle = Bundle(for: cellClass.self)
    if bundle.path(forResource: cellClass.reuseID, ofType: "nib") != nil {
        let nib = UINib(nibName: cellClass.reuseID, bundle: bundle)
        register(nib, forCellReuseIdentifier: cellClass.reuseID)
    } else {
        register(cellClass.self, forCellReuseIdentifier: cellClass.reuseID)
    }
}

Erläuterung:

  1. Reusable Protokoll generiert die Zellen-ID Reusable seines Klassennamens. Stelle sicher, dass du der Konvention cell ID == class name == nib name : cell ID == class name == nib name .
  2. UITableViewCell entspricht dem Reusable Protokoll.
  3. UITableView Erweiterung abstrahiert den Unterschied bei der Registrierung von Zellen über Nib oder Klasse.

Verwendungsbeispiel:

override func viewDidLoad() {
    super.viewDidLoad()
    let tableView = UITableView()
    let cellClasses: [UITableViewCell.Type] = [PostCell.self, ProfileCell.self, CommentCell.self]
    cellClasses.forEach(tableView.register)
}

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: PostCell.self.reuseID) as? PostCell
    ...
    return cell
}

Hier ist meine Methode dafür: Laden von benutzerdefinierten UITableViewCells aus XIB-Dateien ... Noch eine andere Methode

Die Idee besteht darin, eine SampleCell-Unterklasse der UITableViewCell mit einer IBOutlet UIView *content und einer Eigenschaft für jede benutzerdefinierte Unteransicht zu erstellen, die Sie aus dem Code konfigurieren müssen. Dann erstellen Sie eine SampleCell.xib-Datei. Ändern Sie in dieser NIB-Datei den Dateibesitzer in SampleCell. Fügen Sie einen UIView Inhalt UIView , der Ihren Anforderungen entspricht. Fügen Sie alle gewünschten Unteransichten (Beschriftung, Bildansichten, Schaltflächen usw.) hinzu und konfigurieren Sie sie. Verknüpfen Sie schließlich die Inhaltsansicht und die Untersichten mit dem Dateibesitzer.


Ich habe Bentfords Methode # 2 benutzt :

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BDCustomCell"];
    if (cell == nil) {
        // Load the top-level objects from the custom cell XIB.
        NSArray *topLevelObjects = [[NSBundle mainBundle] loadNibNamed:@"BDCustomCell" owner:self options:nil];
        // Grab a pointer to the first object (presumably the custom cell, as that's all the XIB should contain).
        cell = [topLevelObjects objectAtIndex:0];
    }

    return cell;
}

Es funktioniert, aber achten Sie auf Verbindungen zu Dateibesitzer in Ihrer benutzerdefinierten UITableViewCell .xib-Datei.

Durch die Übergabe von owner:self in Ihrer loadNibNamed Anweisung legen Sie den UITableViewController als Besitzer der Datei Ihrer UITableViewCell .

Wenn Sie Drag & Drop in die Header-Datei in IB ziehen, um Aktionen und Outlets einzurichten, werden sie standardmäßig als Besitzer der Datei eingerichtet.

In loadNibNamed:owner:options wird Apples Code versuchen, Eigenschaften auf Ihrem UITableViewController , da dies der Eigentümer ist. Da diese Eigenschaften jedoch nicht definiert sind, erhalten Sie einen Fehler bezüglich der Schlüsselwertcodierung :

*** Terminating app due to uncaught exception 'NSUnknownKeyException', reason:     '[<MyUITableViewController 0x6a383b0> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key myLabel.'

Wenn stattdessen ein Event ausgelöst wird, erhalten Sie eine NSInvalidArgumentException:

-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[MyUITableViewController switchValueDidChange:]: unrecognized selector sent to instance 0x8e9acd0'
*** First throw call stack:
(0x1903052 0x15eed0a 0x1904ced 0x1869f00 0x1869ce2 0x1904ec9 0x5885c2 0x58855a 0x62db76 0x62e03f 0x77fa6c 0x24e86d 0x18d7966 0x18d7407 0x183a7c0 0x1839db4 0x1839ccb 0x1f8b879 0x1f8b93e 0x585a9b 0xb904d 0x2c75)
terminate called throwing an exceptionCurrent language:  auto; currently objective-c

Eine einfache Problemumgehung besteht darin, Ihre Interface Builder-Verbindungen auf die UITableViewCell anstatt auf den UITableViewCell zu verweisen:

  1. Klicken Sie mit der rechten Maustaste auf den Besitzer der Datei, um die Liste der Verbindungen aufzurufen
  2. Nehmen Sie eine Bildschirmaufnahme mit Befehl-Umschalt-4 auf (ziehen Sie, um den zu erfassenden Bereich auszuwählen)
  3. x die Verbindungen vom Eigentümer der Datei aus
  4. Klicken Sie mit der rechten Maustaste auf die UITableCell in der Objekthierarchie und fügen Sie die Verbindungen erneut hinzu.

Ich habe mich entschieden zu posten, da ich keine dieser Antworten mag - die Dinge können immer einfacher sein und dies ist bei weitem die prägnanteste Art, die ich gefunden habe.

1. Erstellen Sie Ihr Xib in Interface Builder, wie Sie es mögen

  • Setzen Sie den Eigentümer der Datei auf die Klasse NSObject
  • Fügen Sie ein UITableViewCell hinzu und legen Sie seine Klasse auf MyTableViewCellSubclass fest - wenn Ihr IB abstürzt (geschieht in Xcode> 4 zum Zeitpunkt des Schreibens), verwenden Sie einfach eine UIView, um die Schnittstelle in Xcode 4 zu erstellen, wenn Sie sie noch herumliegen haben
  • Legen Sie Ihre Subviews in dieser Zelle an und hängen Sie Ihre IBOutlet-Verbindungen an Ihre @ Schnittstelle in der .h oder .m (.m ist meine Präferenz)

2. In Ihrer UIViewController- oder UITableViewController-Unterklasse

@implementation ViewController

static NSString *cellIdentifier = @"MyCellIdentier";

- (void) viewDidLoad {

    ...
    [self.tableView registerNib:[UINib nibWithNibName:@"MyTableViewCellSubclass" bundle:nil] forCellReuseIdentifier:cellIdentifier];
}

- (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    MyTableViewCellSubclass *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];

    ...

    return cell;
}

3. In Ihrer MyTableViewCellSubclass

- (id) initWithCoder:(NSCoder *)aDecoder {
    if (self = [super initWithCoder:aDecoder]) {
        ...
    }

    return self;
}

Importieren Sie zunächst Ihre benutzerdefinierte Zelle #import "CustomCell.h" und ändern Sie dann die Delegate-Methode wie #import "CustomCell.h" :

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

static NSString *simpleTableIdentifier = @"CustomCell";

CustomCell *cell = (CustomCell *)[tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil)
{
    NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"CustomCell" owner:self options:nil];
    cell = [nib objectAtIndex:0];

    [cell setSelectionStyle:UITableViewCellSelectionStyleNone];
}         

     return cell;
}

Nahm Shawn Cravers Antwort und räumte ein bisschen auf.

BBCell.h:

#import <UIKit/UIKit.h>

@interface BBCell : UITableViewCell {
}

+ (BBCell *)cellFromNibNamed:(NSString *)nibName;

@end

BBCell.m:

#import "BBCell.h"

@implementation BBCell

+ (BBCell *)cellFromNibNamed:(NSString *)nibName {
    NSArray *nibContents = [[NSBundle mainBundle] loadNibNamed:nibName owner:self options:NULL];
    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    BBCell *customCell = nil;
    NSObject* nibItem = nil;
    while ((nibItem = [nibEnumerator nextObject]) != nil) {
        if ([nibItem isKindOfClass:[BBCell class]]) {
            customCell = (BBCell *)nibItem;
            break; // we have a winner
        }
    }
    return customCell;
}

@end

Ich mache alle meine UITableViewCells Unterklassen von BBCell und ersetze dann den Standard

cell = [[[BBDetailCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:@"BBDetailCell"] autorelease];

mit:

cell = (BBDetailCell *)[BBDetailCell cellFromNibNamed:@"BBDetailCell"];

Was ich dafür mache, ist eine IBOutlet UITableViewCell *cell in Ihrer Controller-Klasse zu deklarieren. NSBundle loadNibNamed dann die NSBundle loadNibNamed Klassenmethode auf, die die UITableViewCell der oben deklarierten Zelle UITableViewCell .

Für die Xib werde ich eine leere Xib erstellen und das UITableViewCell Objekt in IB hinzufügen, wo es nach Bedarf eingerichtet werden kann. Diese Ansicht wird dann mit der Zelle IBOutlet in der Controller-Klasse verbunden.

- (UITableViewCell *)tableView:(UITableView *)table
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    NSLog(@"%@ loading RTEditableCell.xib", [self description] );

    static NSString *MyIdentifier = @"editableCellIdentifier";
    cell = [table dequeueReusableCellWithIdentifier:MyIdentifier];

    if(cell == nil) {
        [[NSBundle mainBundle] loadNibNamed:@"RTEditableCell"
                                      owner:self
                                    options:nil];
    }

    return cell;
}

NSBundle-Zusätze loadNibNamed (ADC-Login)

ccoaawithlove.com article Ich habe das Konzept von (erhalten Sie die Telefonnummern Beispiel App)


Wenn Sie zum Erstellen von Zellen den Interface Builder verwenden, überprüfen Sie, ob Sie den Bezeichner im Bereich "Informationen" festgelegt haben. Überprüfen Sie dann, ob es beim Aufrufen von dequeueReusableCellWithIdentifier gleich ist.

Ich habe versehentlich vergessen, einige Identifikatoren in einem tabellenlastigen Projekt zu setzen, und die Leistungsänderung war wie Tag und Nacht.





uitableview