Objective-C:如何将ViewController数据传递给嵌套TableView的Cell
嘿,这个嵌套TableView展示问答列表的场景我之前做过好多次了,其实核心是把数据和UI职责分清楚,别让ViewController包揽所有逻辑,给你一步步拆解解决方案:
第一步:定义清晰的数据模型
先创建两个模型类,把问题和答案的数据结构封装起来,这样后续处理数据会非常顺手:
QuestionModel(问题模型)
// QuestionModel.h #import <Foundation/Foundation.h> @class AnswerModel; @interface QuestionModel : NSObject @property (nonatomic, copy) NSString *questionId; @property (nonatomic, copy) NSString *questionContent; @property (nonatomic, strong) NSArray<AnswerModel *> *answers; // 从字典转模型的方法,根据你的接口返回格式调整 + (instancetype)modelWithDictionary:(NSDictionary *)dict; @end // QuestionModel.m #import "QuestionModel.h" #import "AnswerModel.h" @implementation QuestionModel + (instancetype)modelWithDictionary:(NSDictionary *)dict { QuestionModel *model = [[self alloc] init]; model.questionId = dict[@"question_id"]; model.questionContent = dict[@"question_content"]; NSMutableArray *answerArr = [NSMutableArray array]; for (NSDictionary *answerDict in dict[@"answers"]) { AnswerModel *answer = [AnswerModel modelWithDictionary:answerDict]; [answerArr addObject:answer]; } model.answers = answerArr; return model; } @end
AnswerModel(答案模型)
// AnswerModel.h #import <Foundation/Foundation.h> @interface AnswerModel : NSObject @property (nonatomic, copy) NSString *answerId; @property (nonatomic, copy) NSString *answerContent; + (instancetype)modelWithDictionary:(NSDictionary *)dict; @end // AnswerModel.m #import "AnswerModel.h" @implementation AnswerModel + (instancetype)modelWithDictionary:(NSDictionary *)dict { AnswerModel *model = [[self alloc] init]; model.answerId = dict[@"answer_id"]; model.answerContent = dict[@"answer_content"]; return model; } @end
第二步:自定义外层TableViewCell,封装内层TableView逻辑
不要让ViewController管内层TableView的数据源/代理,把这部分职责交给外层Cell,实现解耦:
QuestionTableViewCell(外层Cell)
// QuestionTableViewCell.h #import <UIKit/UIKit.h> @class QuestionModel; @interface QuestionTableViewCell : UITableViewCell @property (nonatomic, strong) QuestionModel *questionModel; @end // QuestionTableViewCell.m #import "QuestionTableViewCell.h" #import "QuestionModel.h" #import "AnswerModel.h" @interface QuestionTableViewCell () <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, strong) UITableView *innerTableView; @property (nonatomic, strong) UILabel *questionLabel; @end @implementation QuestionTableViewCell - (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier { self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]; if (self) { [self setupSubviews]; } return self; } - (void)setupSubviews { // 1. 问题标题Label _questionLabel = [[UILabel alloc] init]; _questionLabel.numberOfLines = 0; _questionLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium]; [self.contentView addSubview:_questionLabel]; // 2. 内层答案TableView _innerTableView = [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStylePlain]; _innerTableView.dataSource = self; _innerTableView.delegate = self; _innerTableView.scrollEnabled = NO; // 禁用滚动,避免和外层冲突 _innerTableView.rowHeight = UITableViewAutomaticDimension; _innerTableView.estimatedRowHeight = 44; _innerTableView.separatorStyle = UITableViewCellSeparatorStyleSingleLine; [self.contentView addSubview:_innerTableView]; // 3. 添加自动布局约束(用SnapKit或者系统约束都可以,这里用系统约束示例) _questionLabel.translatesAutoresizingMaskIntoConstraints = NO; _innerTableView.translatesAutoresizingMaskIntoConstraints = NO; [NSLayoutConstraint activateConstraints:@[ [_questionLabel.topAnchor constraintEqualToAnchor:self.contentView.topAnchor constant:16], [_questionLabel.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16], [_questionLabel.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16], [_innerTableView.topAnchor constraintEqualToAnchor:_questionLabel.bottomAnchor constant:12], [_innerTableView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor constant:16], [_innerTableView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor constant:-16], [_innerTableView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor constant:-16] ]]; } - (void)setQuestionModel:(QuestionModel *)questionModel { _questionModel = questionModel; self.questionLabel.text = questionModel.questionContent; [self.innerTableView reloadData]; // 数据更新后刷新内层TableView } #pragma mark - UITableViewDataSource & Delegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.questionModel.answers.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *cellId = @"AnswerCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellId]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellId]; cell.textLabel.numberOfLines = 0; cell.textLabel.font = [UIFont systemFontOfSize:14]; } AnswerModel *answer = self.questionModel.answers[indexPath.row]; cell.textLabel.text = answer.answerContent; return cell; } @end
第三步:ViewController中的核心逻辑
ViewController只需要管好外层TableView的数据源和数据请求:
// YourViewController.m #import "YourViewController.h" #import "QuestionModel.h" #import "QuestionTableViewCell.h" @interface YourViewController () <UITableViewDataSource, UITableViewDelegate> @property (nonatomic, strong) UITableView *outerTableView; @property (nonatomic, strong) NSArray<QuestionModel *> *questionsArray; @end @implementation YourViewController - (void)viewDidLoad { [super viewDidLoad]; [self setupOuterTableView]; [self fetchDataFromServer]; } - (void)setupOuterTableView { _outerTableView = [[UITableView alloc] initWithFrame:self.view.bounds style:UITableViewStylePlain]; _outerTableView.dataSource = self; _outerTableView.delegate = self; _outerTableView.rowHeight = UITableViewAutomaticDimension; _outerTableView.estimatedRowHeight = 150; // 给一个预估高度,避免跳动 [_outerTableView registerClass:[QuestionTableViewCell class] forCellReuseIdentifier:@"QuestionCell"]; [self.view addSubview:_outerTableView]; } - (void)fetchDataFromServer { // 这里替换成你的网络请求代码,比如用AFNetworking或者NSURLSession NSURL *url = [NSURL URLWithString:@"你的接口地址"]; NSURLRequest *request = [NSURLRequest requestWithURL:url]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (error) { NSLog(@"请求失败:%@", error.localizedDescription); return; } NSError *jsonError; NSDictionary *responseDict = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; if (jsonError) { NSLog(@"解析失败:%@", jsonError.localizedDescription); return; } // 解析数据成QuestionModel数组 NSMutableArray *questions = [NSMutableArray array]; for (NSDictionary *questionDict in responseDict[@"questions"]) { QuestionModel *model = [QuestionModel modelWithDictionary:questionDict]; [questions addObject:model]; } // 回到主线程刷新UI dispatch_async(dispatch_get_main_queue(), ^{ self.questionsArray = questions; [self.outerTableView reloadData]; }); }]; [task resume]; } #pragma mark - UITableViewDataSource & Delegate - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { return self.questionsArray.count; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { QuestionTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"QuestionCell" forIndexPath:indexPath]; cell.questionModel = self.questionsArray[indexPath.row]; return cell; } @end
几个关键的避坑点
- 内层TableView一定要设置
scrollEnabled = NO,否则会和外层TableView的滚动产生冲突,导致交互混乱 - 内外层TableView都用
UITableViewAutomaticDimension配合自动布局,实现高度的自适应,不用手动计算高度 - 自定义Cell初始化时记得关闭
translatesAutoresizingMaskIntoConstraints,否则自动布局会失效
内容的提问来源于stack exchange,提问作者MRizwan33
相关产品推荐
相关产品推荐

