ios - swift xib




你如何從Xib文件加載自定義的UITableViewCells? (14)

問題很簡單:你如何從Xib文件加載自定義的UITableViewCell ? 這樣做可以讓您使用Interface Builder來設計單元格。 顯然,由於內存管理問題,答案並不簡單。 這個線程提到了這個問題,並提出了一個解決方案,但它是在NDA發布之前,並且沒有代碼。 這裡有一個很長的話題,沒有提供明確的答案來討論這個問題。

以下是我使用的一些代碼:

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];
}

要使用此代碼,請創建MyCell.m / .h,這是UITableViewCell一個新子類,並為您需要的組件添加IBOutlets 。 然後創建一個新的“Empty XIB”文件。 在IB中打開Xib文件,添加一個UITableViewCell對象,將其標識符設置為“MyCellIdentifier”,並將其類設置為MyCell並添加組件。 最後,將IBOutlets連接到組件。 請注意,我們沒有在IB中設置文件所有者。

其他方法主張設置文件的所有者並警告內存洩漏,如果Xib未通過額外的工廠類加載。 我在Instruments / Leaks下面測試了上述內容,沒有發現內存洩漏。

那麼從Xibs加載單元格的規範方法是什麼? 我們是否設置文件的所有者? 我們需要工廠嗎? 如果是這樣,工廠的代碼是什麼樣的? 如果有多種解決方案,讓我們來澄清他們每個人的優點和缺點......


寄存器

iOS 7之後,這個過程被簡化為( swift 3.0 ):

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

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

)這也可以通過在.stroyboard.stroyboard文件中創建單元格作為原型單元格來實現。 如果你需要給它們附上一個類,你可以選擇單元格原型並添加相應的類(當然,它必須是UITableViewCell的後代)。

出列

之後,使用( swift 3.0 )出列:

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

    cell.textLabel?.text = "Hello"

    return cell
}

不同之處在於,這種新方法不僅使單元出隊,而且還創建了不存在的情況(這意味著if (cell == nil) shenanigans不需要做),並且單元準備好使用就像在上面的例子中。

警告tableView.dequeueReusableCell(withIdentifier:for:)具有新的行為,如果你調用另一個(沒有indexPath: tableView.dequeueReusableCell(withIdentifier:for:) ,你會得到舊行為,你需要檢查nil並自己實例化,注意UITableViewCell? 返回值。

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

當然,單元的關聯類的類型是您在.xib文件中為UITableViewCell子類定義的類型,或者使用其他註冊方法。

組態

理想情況下,您的單元格在註冊時已經按照外觀和內容定位(如標籤和圖像視圖)進行了配置,在cellForRowAtIndexPath方法中,您只需填寫它們即可。

全部一起

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
    }
}

當然,這在ObjC中都有相同的名稱。


  1. UITableViewCell創建您自己的定制類AbcViewCell子類(確保您的類文件名和nib文件名相同)

  2. 創建這個擴展類方法。

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

    let cell: AbcViewCell = UITableViewCell.fromNib()


以下是在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)
    }
}

說明:

  1. Reusable協議從其類名生成單元ID。 確保你遵循約定: cell ID == class name == nib name
  2. UITableViewCell符合Reusable協議。
  3. UITableView擴展通過nib或class抽象化註冊單元格的差異。

用法示例:

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
}

以下是我用來從XIB創建自定義單元格的類方法:

+ (CustomCell*) createNewCustomCellFromNib {

    NSArray* nibContents = [[NSBundle mainBundle]
                            loadNibNamed:@"CustomCell" owner:self options:NULL];

    NSEnumerator *nibEnumerator = [nibContents objectEnumerator];
    CustomCell *customCell= nil;
    NSObject* nibItem = nil;

    while ( (nibItem = [nibEnumerator nextObject]) != nil) {

        if ( [nibItem isKindOfClass: [CustomCell class]]) {
            customCell = (CustomCell*) nibItem;

            if ([customCell.reuseIdentifier isEqualToString: @"CustomCell"]) {
                break; // we have a winner
            }
            else
                fuelEntryCell = nil;
        }
    }
    return customCell;
}

然後,在XIB中,我設置類名稱,並重用標識符。 之後,我可以在視圖控制器中調用該方法而不是

[[UITableViewCell] alloc] initWithFrame:]

它足夠快,並在我的兩個發貨應用程序中使用。 這比調用[nib objectAtIndex:0]更可靠,在我看來,至少比Stephan Burlot的例子更可靠,因為你保證只從一個正確類型的XIB中獲取視圖。


從XIB中加載UITableViewCells可以節省很多代碼,但通常會導致可怕的滾動速度(實際上,它不是XIB,而是過度使用UIViews)。

我建議你看看這個: 鏈接參考


我不知道是否有規範的方式,但這是我的方法:

  • 為ViewController創建一個xib
  • 將文件所有者類設置為UIViewController
  • 刪除視圖並添加一個UITableViewCell
  • 將您的UITableViewCell的類設置為您的自定義類
  • 設置你的UITableViewCell的標識符
  • 將視圖控制器視圖的出口設置為您的UITableViewCell

並使用此代碼:

MyCustomViewCell *cell = (MyCustomViewCell *)[_tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
  UIViewController* c = [[UIViewController alloc] initWithNibName:CellIdentifier bundle:nil];
  cell = (MyCustomViewCell *)c.view;
  [c release];
}

在你的例子中,使用

[nib objectAtIndex:0]

如果Apple更改xib中項目的順序,則可能會中斷。


我所做的是在你的控制器類中聲明一個IBOutlet UITableViewCell *cell 。 然後調用NSBundle loadNibNamed類方法,它將UITableViewCell給上面聲明的單元格。

對於xib,我將創建一個空的xib,並在IB中添加UITableViewCell對象,以便根據需要進行設置。 這個視圖然後連接到控制器類中的單元IBOutlet

- (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添加loadNibNamed(ADC登錄)

cocoawithlove.com文章我找到了這個概念(獲取電話號碼示例應用程序)


我決定發布,因為我不喜歡任何這些答案 - 事情總是可以更簡單,這是迄今為止我找到的最簡潔的方式。

1.按照你的喜好在Interface Builder中構建你的Xib

  • 將文件的所有者設置為類NSObject
  • 添加一個UITableViewCell並將其類設置為MyTableViewCellSubclass - 如果您的IB崩潰(在本文寫作時發生在Xcode> 4中),只需使用UIView做Xcode 4中的接口,如果您仍然在使用它
  • 在此單元格內佈局子視圖並將IBOutlet連接附加到.h或.m中的@interface(.m是我的偏好)

2.在你的UIViewController或UITableViewController子類中

@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.在你的MyTableViewCellSubclass中

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

    return self;
}

檢查這個 - http://eppz.eu/blog/custom-uitableview-cell/ - 使用一個微型的類,在控制器實現中結束一行真的很方便:

-(UITableViewCell*)tableView:(UITableView*) tableView cellForRowAtIndexPath:(NSIndexPath*) indexPath
{
    return [TCItemCell cellForTableView:tableView
                          atIndexPath:indexPath
                      withModelSource:self];
}


正確的做法是創建一個UITableViewCell子類實現,頭文件和XIB。 在XIB中刪除任何視圖並添加一個表格單元格。 將該類設置為UITableViewCell子類的名稱。 對於文件所有者,使其成為UITableViewController子類的名稱。 使用tableViewCell插座將文件所有者連接到單元格。

在頭文件中:

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

在實現文件中:

@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;
}

正確的解決方案是這樣的:

- (void)viewDidLoad
{
 [super viewDidLoad];
 UINib *nib = [UINib nibWithNibName:@"ItemCell" bundle:nil];
 [[self tableView] registerNib:nib forCellReuseIdentifier:@"ItemCell"];
}

-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
   // Create an instance of ItemCell
   PointsItemCell *cell =  [tableView dequeueReusableCellWithIdentifier:@"ItemCell"];

return cell;
}

這個擴展需要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
    }
}

創建一個僅包含1個自定義UITableViewCell的Xib文件。

加載它。

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

重新加載NIB是昂貴的。 最好加載一次,然後在需要單元格時實例化對象。 請注意,您可以使用此方法(Apple的“registerNIB”iOS5僅允許一個頂級對象 - Bug 10580062“iOS5 tableView registerNib:過度限制”)將UIImageViews等添加到筆尖,甚至多個單元格。

所以我的代碼如下 - 你只需要在NIB中讀取一次(像初始化一樣,或者在viewDidload中 - 不管怎麼樣),然後你將實例化nib到對像中,然後選擇你需要的那個,這比加載nib更有效率一遍又一遍。

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;
}

首先導入您的自定義單元格文件#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;
}






uitableview