在 tableView:heightForRowAtIndexPath: 中呼叫 [self.tableView cellForRowAtIndexPath:indexPath] 取得 Cell 判斷高度,導致無限遞迴崩潰(heightForRow → cellForRow → 需要高度 → heightForRow...)。
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; // 致命錯誤!
if ([cell.reuseIdentifier isEqualToString:@"EventWICellIdentifier"]) return 112;
// ...
}
絕不在 heightForRow 中呼叫 cellForRow。高度應由資料模型(而非視圖)決定:
// 模型
@interface EventItem : NSObject
@property (nonatomic, strong) NSString *type; // "EventWI", "EventWOI", "PastEventWI", "PastEventWOI"
@property (nonatomic, assign) CGFloat height; // 或根據型別計算
@end
// 代理
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
EventItem *item = self.dataSource[indexPath.row];
if ([item.type isEqualToString:@"EventWI"] || [item.type isEqualToString:@"EventWOI"]) return 112;
return 56;
}
- (void)viewDidLoad {
[super viewDidLoad];
tableView.estimatedRowHeight = 80; // 預估高度
tableView.rowHeight = UITableViewAutomaticDimension; // 自動計算
}
// Cell 內部使用 Auto Layout 完整約束,系統自動計算高度
// 完全不需要實作 heightForRowAtIndexPath
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
NSString *reuseId = [self tableView:tableView cellForRowAtIndexPath:indexPath].reuseIdentifier; // 仍錯誤!
// 正確:從資料來源決定 reuseIdentifier
EventItem *item = self.dataSource[indexPath.row];
NSString *reuseId = [self reuseIdentifierForItem:item];
static NSDictionary *heightMap = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
heightMap = @{
@"PastEventWICellIdentifier": @56,
@"PastEventWOICellIdentifier": @56,
@"EventWICellIdentifier": @112,
@"EventWOICellIdentifier": @112
};
});
return [heightMap[reuseId] floatValue];
}
tableView.estimatedRowHeight 設定合理預估值。tableView.rowHeight = UITableViewAutomaticDimension。heightForRow,且嚴禁呼叫 cellForRow。理解 TableView 佈局渲染流程與代理調用順序,是避免死循環、卡頓、崩潰的關鍵。