API含多JSON键,如何根据verb字段展示不同UICollectionViewCell?
实现根据verb字段展示不同UICollectionViewCell的方案(纯代码+MVC)
这问题我熟,纯代码结合MVC架构的实现思路很清晰,我给你一步步拆解:
1. 创建不同的Cell子类
首先你需要为"shared"和"posted"分别创建专属的UICollectionViewCell子类,纯代码实现布局,各自负责自己的UI展示:
// SharedCell.swift class SharedCell: UICollectionViewCell { // 定义Cell的专属UI元素 private let sharedLabel: UILabel = { let label = UILabel() label.text = "Shared Content" label.font = UIFont.systemFont(ofSize: 14, weight: .medium) label.translatesAutoresizingMaskIntoConstraints = false return label }() private let contentImageView: UIImageView = { let iv = UIImageView() iv.contentMode = .scaleAspectFill iv.clipsToBounds = true iv.translatesAutoresizingMaskIntoConstraints = false return iv }() override init(frame: CGRect) { super.init(frame: frame) setupLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupLayout() { contentView.addSubview(sharedLabel) contentView.addSubview(contentImageView) NSLayoutConstraint.activate([ sharedLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), sharedLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8), contentImageView.topAnchor.constraint(equalTo: sharedLabel.bottomAnchor, constant: 8), contentImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor), contentImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor), contentImageView.heightAnchor.constraint(equalToConstant: 150) ]) } // 配置方法,符合MVC解耦原则(ViewController不直接操作Cell子视图) func configure(with model: myModel) { sharedLabel.text = "Shared at \(model.date ?? "")" if let urlString = model.image_url, let url = URL(string: urlString) { // 这里可以用Kingfisher等库加载图片,或者自定义扩展实现 contentImageView.load(url: url) } } } // PostedCell.swift class PostedCell: UICollectionViewCell { // 定义Cell的专属UI元素 private let postedLabel: UILabel = { let label = UILabel() label.text = "Posted Content" label.font = UIFont.systemFont(ofSize: 14, weight: .bold) label.translatesAutoresizingMaskIntoConstraints = false return label }() private let contentTextLabel: UILabel = { let label = UILabel() label.numberOfLines = 0 label.translatesAutoresizingMaskIntoConstraints = false return label }() override init(frame: CGRect) { super.init(frame: frame) setupLayout() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupLayout() { contentView.addSubview(postedLabel) contentView.addSubview(contentTextLabel) NSLayoutConstraint.activate([ postedLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8), postedLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8), contentTextLabel.topAnchor.constraint(equalTo: postedLabel.bottomAnchor, constant: 8), contentTextLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 8), contentTextLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -8), contentTextLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8) ]) } func configure(with model: myModel) { postedLabel.text = "Posted at \(model.date ?? "")" contentTextLabel.text = model.style ?? "No content" } }
2. 在ViewController中注册并配置Cell
在控制器层(ViewController)里完成Cell注册、数据源绑定和类型判断:
class MyViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { private let collectionView: UICollectionView = { let layout = UICollectionViewFlowLayout() layout.minimumLineSpacing = 16 layout.minimumInteritemSpacing = 16 layout.sectionInset = UIEdgeInsets(top: 16, left: 16, bottom: 16, right: 16) let cv = UICollectionView(frame: .zero, collectionViewLayout: layout) cv.translatesAutoresizingMaskIntoConstraints = false cv.backgroundColor = .white return cv }() // 存储API返回的模型数据 private var dataSource: [myModel] = [] override func viewDidLoad() { super.viewDidLoad() setupCollectionView() // 这里添加API请求逻辑,解析后填充dataSource并刷新列表 fetchAPIData() } private func setupCollectionView() { view.addSubview(collectionView) // 设置collectionView约束,铺满安全区域 NSLayoutConstraint.activate([ collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor), collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor), collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor), collectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor) ]) // 注册两个Cell collectionView.register(SharedCell.self, forCellWithReuseIdentifier: "SharedCell") collectionView.register(PostedCell.self, forCellWithReuseIdentifier: "PostedCell") // 设置数据源和代理 collectionView.dataSource = self collectionView.delegate = self } private func fetchAPIData() { // 这里写你的API请求+JSON解析逻辑 // 示例:解析完成后更新数据源并刷新 // dataSource = parsedModels // collectionView.reloadData() } // MARK: - UICollectionView DataSource func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { return dataSource.count } func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { let model = dataSource[indexPath.item] // 处理verb可选值,避免空值崩溃 guard let verb = model.verb else { // 可自定义默认Cell返回,这里用系统默认Cell占位 return UICollectionViewCell() } switch verb { case "shared": let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SharedCell", for: indexPath) as! SharedCell cell.configure(with: model) return cell case "posted": let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "PostedCell", for: indexPath) as! PostedCell cell.configure(with: model) return cell default: // 其他verb值的处理,可返回默认Cell或隐藏 return UICollectionViewCell() } } // MARK: - UICollectionView DelegateFlowLayout func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { let model = dataSource[indexPath.item] let width = view.frame.width - 32 switch model.verb { case "shared": return CGSize(width: width, height: 200) case "posted": return CGSize(width: width, height: 120) default: return CGSize(width: width, height: 100) } } }
3. 关键注意点
- MVC边界:模型
myModel只负责存储数据,ViewController处理数据逻辑和协调,Cell自己管理UI展示,避免跨层操作。 - 可选值安全:模型中
verb是可选类型,必须先解包再判断,防止空值崩溃。 - Cell复用规范:注册的复用标识符必须和dequeue时一致,避免复用错误。
- 图片加载优化:建议用Kingfisher、SDWebImage等第三方库处理图片加载,自带缓存和错误处理,比原生更高效。
内容的提问来源于stack exchange,提问作者Boxo
相关产品推荐
相关产品推荐

