You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 09:08:46