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

Swift 4.0中基于TableViewCell值的动态CollectionView实现问题

Hey there! Let's walk through how to build this nested TableView + CollectionView setup for your showtimes data. I'll break it down step by step so it's easy to follow, even if you're new to Swift.


Step 1: Define a Data Model with Codable

First, we'll create a struct to represent each showtime entry from your JSON. Using Codable makes parsing the JSON super straightforward, and we'll map the uppercase JSON keys to Swift-style lowercase properties:

struct Showtime: Codable {
    let screentime: String
    let format: String
    
    // Map uppercase JSON keys to our lowercase properties
    enum CodingKeys: String, CodingKey {
        case screentime = "SCREENTIME"
        case format = "FORMAT"
    }
}
Step 2: Group Your Showtimes by Format

Next, we need to group the raw showtimes array by the FORMAT value. This lets us create one TableView row per format, with all matching screentimes in its embedded CollectionView.

In your ViewController, declare a grouped data source property:

// Stores (format name, array of screentimes) pairs
var showtimeGroups: [(format: String, times: [String])] = []

Then parse your JSON and group the data in viewDidLoad():

override func viewDidLoad() {
    super.viewDidLoad()
    
    // Your raw JSON string (replace with your actual data source)
    let jsonString = """
    [{"SCREENTIME": "15:00", "FORMAT": "2D"}, {"SCREENTIME": "17:00", "FORMAT": "2D"}, {"SCREENTIME": "21:00", "FORMAT": "2D"}, {"SCREENTIME": "12:00", "FORMAT": "3D"}, {"SCREENTIME": "14:00", "FORMAT": "3D"}]
    """
    
    guard let jsonData = jsonString.data(using: .utf8) else {
        print("Failed to convert JSON string to data")
        return
    }
    
    do {
        // Parse JSON into an array of Showtime objects
        let rawShowtimes = try JSONDecoder().decode([Showtime].self, from: jsonData)
        
        // Group showtimes by their format
        let groupedDictionary = Dictionary(grouping: rawShowtimes) { $0.format }
        
        // Convert the dictionary into a sorted array for consistent TableView ordering
        showtimeGroups = groupedDictionary
            .map { (format: $0.key, times: $0.value.map { $0.screentime }) }
            .sorted { $0.format < $1.format } // Sort alphabetically (2D comes before 3D)
        
        tableView.reloadData()
    } catch {
        print("JSON parsing error: \(error.localizedDescription)")
    }
    
    // Register your custom TableViewCell
    tableView.register(ShowtimeTableViewCell.self, forCellReuseIdentifier: "ShowtimeCell")
    tableView.dataSource = self
}
Step 3: Build the Custom TableViewCell with Embedded CollectionView

We'll create a custom TableViewCell that contains a horizontal CollectionView to display screentimes. First, the CollectionView cell:

class TimeCollectionViewCell: UICollectionViewCell {
    let timeLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.textAlignment = .center
        label.font = .systemFont(ofSize: 14)
        return label
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupView()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupView() {
        contentView.addSubview(timeLabel)
        NSLayoutConstraint.activate([
            timeLabel.centerXAnchor.constraint(equalTo: contentView.centerXAnchor),
            timeLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
        ])
        
        // Add some styling for better visibility
        contentView.layer.cornerRadius = 8
        contentView.layer.borderWidth = 1
        contentView.layer.borderColor = UIColor.systemGray.cgColor
    }
}

Now the TableViewCell that holds the CollectionView:

class ShowtimeTableViewCell: UITableViewCell {
    let collectionView: UICollectionView = {
        let layout = UICollectionViewFlowLayout()
        layout.scrollDirection = .horizontal
        layout.itemSize = CGSize(width: 80, height: 40)
        layout.minimumInteritemSpacing = 10
        layout.minimumLineSpacing = 10
        
        let cv = UICollectionView(frame: .zero, collectionViewLayout: layout)
        cv.translatesAutoresizingMaskIntoConstraints = false
        cv.backgroundColor = .white
        return cv
    }()
    
    // Data source for the CollectionView
    var screentimes: [String] = [] {
        didSet {
            collectionView.reloadData()
        }
    }
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        setupCell()
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func setupCell() {
        contentView.addSubview(collectionView)
        
        // Pin the CollectionView to the cell's content with padding
        NSLayoutConstraint.activate([
            collectionView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 8),
            collectionView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
            collectionView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
            collectionView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -8),
            collectionView.heightAnchor.constraint(equalToConstant: 56)
        ])
        
        // Register the CollectionView cell and set delegates
        collectionView.register(TimeCollectionViewCell.self, forCellWithReuseIdentifier: "TimeCell")
        collectionView.dataSource = self
        collectionView.delegate = self
    }
}

// MARK: - CollectionView DataSource & Delegate
extension ShowtimeTableViewCell: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return screentimes.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "TimeCell", for: indexPath) as? TimeCollectionViewCell else {
            return UICollectionViewCell()
        }
        cell.timeLabel.text = screentimes[indexPath.item]
        return cell
    }
}
Step 4: Connect Everything in Your ViewController

Finally, implement the TableView data source methods to populate each row with the correct format and screentimes:

extension ViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return showtimeGroups.count
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "ShowtimeCell", for: indexPath) as? ShowtimeTableViewCell else {
            return UITableViewCell()
        }
        let group = showtimeGroups[indexPath.row]
        cell.textLabel?.text = group.format // Show the format in the cell's title
        cell.screentimes = group.times // Pass screentimes to the embedded CollectionView
        return cell
    }
    
    func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
        return 72 // Match the CollectionView height + padding
    }
}

That's it! This setup will create a TableView where each row shows a format (2D/3D), and a horizontal scrollable CollectionView inside each row displays all matching screentimes. You can tweak the styling (colors, sizes, spacing) to fit your app's design.

内容的提问来源于stack exchange,提问作者vanillatte

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 11:32:47