【问题标题】:[Programmatically]Push a viewController when selecting tableView row embedded in a collectionView cell[以编程方式]选择嵌入在 collectionView 单元格中的 tableView 行时推送 viewController
【发布时间】:2022-01-14 10:31:41
【问题描述】:

当用户单击 tableView 单元格时,我想推送一个视图控制器,以显示用户任务的详细视图。我尝试使用弹出视图,因为我不知道如何从 collectionView 单元中推送 viewController,但我无法将 didselect 连接到 MyTasksDetailController 布局视图。这是我的代码:

MyTasksCollectionCell

class MyTasksCollectionCell: UICollectionViewCell, UITableViewDelegate, UITableViewDataSource {

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
                 tableView.deselectRow(at: indexPath, animated: true)

    let detailView = MyTasksDetailController()
    UIApplication.shared.keyWindow?.addSubview(detailView)

// I want the detailViewController to show these two (descriptionTitleLabel + titleLabel)  
//          print("\(task.descriptionTitleLabel)") 
//          print("\(task.titleLabel)") 

}
extension UIApplication {
    
    var keyWindow: UIWindow? {
        // Get connected scenes
        return UIApplication.shared.connectedScenes
            // Keep only active scenes, onscreen and visible to the user
            .filter { $0.activationState == .foregroundActive }
            // Keep only the first `UIWindowScene`
            .first(where: { $0 is UIWindowScene })
            // Get its associated windows
            .flatMap({ $0 as? UIWindowScene })?.windows
            // Finally, keep only the key window
            .first(where: \.isKeyWindow)
    }
}

MyTasksDetailController

class MyTasksDetailController: UIView  {
    
    var setTitleLabel: String? {
        didSet {
            titleLabel.text = setTitleLabel ?? ""
        }
    }
    var setdescriptionTitleLabel: String? {
        didSet {
            descriptionTitleLabel.text = setdescriptionTitleLabel ?? ""
        }
    }
     let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 28, weight: .bold)
        label.textAlignment = .center
        return label
    }()
    
     let descriptionTitleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
        label.textAlignment = .center
        label.numberOfLines = 3
        return label
    }()
    
     let container: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.clipsToBounds = true
        v.backgroundColor = .white
        v.backgroundColor =
            // 1
            UIColor { traitCollection in
              // 2
              switch traitCollection.userInterfaceStyle {
              case .dark:
                // 3
                 v.layer.borderColor = UIColor.label.cgColor
                return UIColor.systemBackground
                
              default:
                // 4
                 v.layer.borderColor = UIColor.black.cgColor
                return UIColor.systemBackground
              }
            }
        return v
    }()
    
     lazy var stack: UIStackView = {
        let stack = UIStackView(arrangedSubviews: [titleLabel, descriptionTitleLabel])
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.axis = .vertical
        return stack
    }()
    @objc func animateOut() {
        UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
            self.container.transform = CGAffineTransform(translationX: self.frame.height, y: 0)
            self.alpha = 0
        }) { (complete) in
            if complete {
                self.removeFromSuperview()
            }
        }
    }
    @objc func animateIn() {
        self.container.transform = CGAffineTransform(translationX: self.frame.height, y: 0)
        self.alpha = 1
        UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 0, options: .curveEaseOut, animations: {
            self.container.transform = .identity
            self.alpha = 1
        })
    }
    override init(frame: CGRect) {
        super.init(frame: frame)
         self.frame = UIScreen.main.bounds
        self.addSubview(container)

        self.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(animateOut)))
        container.topAnchor.constraint(equalTo:  self.topAnchor, constant: 0).isActive = true
        container.bottomAnchor.constraint(equalTo:  self.bottomAnchor, constant: 0).isActive = true
        container.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true

         
        container.addSubview(stack)
        stack.leadingAnchor.constraint(equalTo: container.leadingAnchor).isActive = true
        stack.trailingAnchor.constraint(equalTo: container.trailingAnchor).isActive = true
        stack.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
        stack.heightAnchor.constraint(equalTo: container.heightAnchor, multiplier: 0.5).isActive = true
        animateIn()
     }
    
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

【问题讨论】:

  • MyTasksDetailControllerUIView,而不是UIViewController ...您在导航控制器中并且想要push 一个“详细视图控制器”到堆栈上吗?或者,您想present 一个“详细视图控制器”吗?还是只想将MyTasksDetailController 添加为子视图?
  • @DonMag 我不在导航控制器中,是的,我想展示一个详细的视图控制器

标签: ios swift xcode uitableview uicollectionviewcell


【解决方案1】:

首先,如果你想展示你的MyTasksDetailController,它必须是UIViewController,而不是UIView

您可以像这样呈现它(从您的主视图控制器):

// instantiate MyTasksDetailController
let vc = MyTasksDetailController()
// set the title and description string properties
vc.titleString = "Some title"
vc.descriptionString = "Some description"
// present it
self.present(vc, animated: true, completion: nil)

要从集合视图单元格中获取选定的表格行,您可以使用closure

在你的集合视图单元类中,定义闭包:

var myClosure: ((MyTasksCollectionCell, Int) -> ())?

当您在表格视图中选择一行时:

func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
    // when a row is selected, use the
    //  Closure to inform the controller
    myClosure?(self, indexPath.row)
}

你的 cellForItemAt 函数看起来像这样:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myTasksCollectionCell", for: indexPath) as! MyTasksCollectionCell
    
    cell.label.text = myData[indexPath.item].title
    cell.tableData = myData[indexPath.item].tasks
    
    // set closure
    cell.myClosure = { [weak self] theCell, theRow in
        guard let self = self,
              let theIndexPath = self.collectionView.indexPath(for: theCell)
        else {
            return
        }
        // instantiate MyTasksDetailController
        let vc = MyTasksDetailController()
        // set the title and description string properties
        vc.titleString = self.myData[theIndexPath.item].title
        vc.descriptionString = self.myData[theIndexPath.item].tasks[theRow]
        // present it
        self.present(vc, animated: true, completion: nil)
    }
    return cell
}

这是一个完整的示例,您可以运行和使用...

细节控制器的修改版本

class MyTasksDetailController: UIViewController  {
    
    var titleString: String = ""
    var descriptionString: String = ""
    
    let titleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 28, weight: .bold)
        label.textAlignment = .center
        label.backgroundColor = .green
        return label
    }()
    
    let descriptionTitleLabel: UILabel = {
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.font = UIFont.systemFont(ofSize: 18, weight: .bold)
        label.textAlignment = .center
        label.numberOfLines = 3
        label.backgroundColor = .cyan
        return label
    }()
    
    let container: UIView = {
        let v = UIView()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.clipsToBounds = true
        v.backgroundColor = .white
        v.backgroundColor =
        // 1
        UIColor { traitCollection in
            // 2
            switch traitCollection.userInterfaceStyle {
            case .dark:
                // 3
                v.layer.borderColor = UIColor.label.cgColor
                return UIColor.systemBackground
                
            default:
                // 4
                v.layer.borderColor = UIColor.black.cgColor
                return UIColor.systemBackground
            }
        }
        return v
    }()
    
    lazy var stack: UIStackView = {
        let stack = UIStackView(arrangedSubviews: [titleLabel, descriptionTitleLabel])
        stack.translatesAutoresizingMaskIntoConstraints = false
        stack.axis = .vertical
        return stack
    }()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        view.addSubview(container)
        
        view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(dismissMe)))
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            container.topAnchor.constraint(equalTo: g.topAnchor, constant: 0),
            container.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0),
            container.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0),
            container.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0),
        ])
        
        container.addSubview(stack)
        NSLayoutConstraint.activate([
            stack.leadingAnchor.constraint(equalTo: container.leadingAnchor, constant: 0),
            stack.trailingAnchor.constraint(equalTo: container.trailingAnchor, constant: 0),
            stack.centerYAnchor.constraint(equalTo: container.centerYAnchor, constant: 0),
            stack.heightAnchor.constraint(equalTo: container.heightAnchor, multiplier: 0.5),
        ])

        titleLabel.text = titleString
        descriptionTitleLabel.text = descriptionString
    }
    
    @objc func dismissMe() {
        dismiss(animated: true, completion: nil)
    }
    
}

简单的数据结构

struct MyTask {
    var title: String = ""
    var tasks: [String] = []
}

“主”视图控制器(保存集合视图):

class MyTasksViewController: UIViewController {
    
    var myData: [MyTask] = []
    
    let colors: [UIColor] = [
        .systemRed, .systemGreen, .systemBlue,
        .systemPink, .systemYellow, .systemTeal,
    ]
    
    var collectionView: UICollectionView!
    
    override func viewDidLoad() {
        super.viewDidLoad()

        let tableStrings: [[String]] = [
            ["red", "green", "blue", "cyan", "magenta", "yellow"],
            ["one", "two", "three"],
            ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"],
            ["Bob", "Joe", "Steve", "Mary"],
            ["Car", "Boat", "Train", "Airplane", "Bicycle"],
        ]
        
        // fill myData array with some data
        for i in 0..<tableStrings.count {
            let mt: MyTask = MyTask(title: "Cell Title \(i)", tasks: tableStrings[i])
            myData.append(mt)
        }
        
        let cvl = UICollectionViewFlowLayout()
        cvl.itemSize = CGSize(width: 200, height: 240)
        cvl.minimumLineSpacing = 10
        cvl.minimumInteritemSpacing = 0
        cvl.scrollDirection = .horizontal
        
        collectionView = UICollectionView(frame: .zero, collectionViewLayout: cvl)
        
        collectionView.translatesAutoresizingMaskIntoConstraints = false
        
        view.addSubview(collectionView)
        
        let g = view.safeAreaLayoutGuide
        
        NSLayoutConstraint.activate([
            
            collectionView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
            collectionView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            collectionView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            collectionView.heightAnchor.constraint(equalToConstant: 240.0),
            
        ])
        
        collectionView.dataSource = self
        collectionView.delegate = self
        
        collectionView.register(MyTasksCollectionCell.self, forCellWithReuseIdentifier: "myTasksCollectionCell")
        
    }
    
}

extension MyTasksViewController: UICollectionViewDataSource, UICollectionViewDelegate {
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return myData.count
    }
    
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "myTasksCollectionCell", for: indexPath) as! MyTasksCollectionCell
        cell.contentView.backgroundColor = colors[indexPath.item % colors.count]
        
        cell.label.text = myData[indexPath.item].title
        cell.tableData = myData[indexPath.item].tasks
        
        // set closure
        cell.myClosure = { [weak self] theCell, theRow in
            guard let self = self,
                  let theIndexPath = self.collectionView.indexPath(for: theCell)
            else {
                return
            }
            // instantiate MyTasksDetailController
            let vc = MyTasksDetailController()
            // set the title and description string properties
            vc.titleString = self.myData[theIndexPath.item].title
            vc.descriptionString = self.myData[theIndexPath.item].tasks[theRow]
            // present it
            self.present(vc, animated: true, completion: nil)
        }
        return cell
    }
}

简单表格视图单元格

// simple table cell with a centered-text label
class MyTasksTableCell: UITableViewCell {
    let label: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.textAlignment = .center
        return v
    }()
    
    override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        contentView.addSubview(label)
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            label.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
        ])
    }
}

Collection View Cell带有“顶部标签”和tableView

// collection view cell with
//  label at the top and
//  table view below the label
class MyTasksCollectionCell: UICollectionViewCell {
    
    // closure to tell the controller that a tableView row was selected
    var myClosure: ((MyTasksCollectionCell, Int) -> ())?
    
    var tableData: [String] = [] {
        didSet {
            tableView.reloadData()
        }
    }
    
    let label: UILabel = {
        let v = UILabel()
        v.translatesAutoresizingMaskIntoConstraints = false
        v.backgroundColor = UIColor(white: 0.9, alpha: 1.0)
        v.textAlignment = .center
        return v
    }()
    
    let tableView: UITableView = {
        let v = UITableView()
        v.translatesAutoresizingMaskIntoConstraints = false
        return v
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        commonInit()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        commonInit()
    }
    func commonInit() -> Void {
        contentView.addSubview(label)
        contentView.addSubview(tableView)
        
        let g = contentView.layoutMarginsGuide
        NSLayoutConstraint.activate([
            label.topAnchor.constraint(equalTo: g.topAnchor, constant: 0.0),
            label.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            label.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            label.heightAnchor.constraint(equalToConstant: 30.0),
            
            tableView.topAnchor.constraint(equalTo: label.bottomAnchor, constant: 0.0),
            tableView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 0.0),
            tableView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 0.0),
            tableView.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: 0.0),
        ])
        
        tableView.separatorInset = .zero
        tableView.register(MyTasksTableCell.self, forCellReuseIdentifier: "myTasksTableCell")
        tableView.dataSource = self
        tableView.delegate = self
    }
}
extension MyTasksCollectionCell: UITableViewDataSource, UITableViewDelegate {
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return tableData.count
    }
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let c = tableView.dequeueReusableCell(withIdentifier: "myTasksTableCell", for: indexPath) as! MyTasksTableCell
        c.label.text = tableData[indexPath.row]
        return c
    }
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        // when a row is selected, use the
        //  Closure to inform the controller
        myClosure?(self, indexPath.row)
    }
}

这是它的样子,集合视图向右滚动了几个单元格:

以及我们选择一行时得到的结果:

【讨论】:

  • 首先感谢您的详细解答。我试过你的代码,它看起来很好,但是当我运行应用程序时它崩溃了,我在 MyTasksViewController 的vc.titleString = self.myData[theIndexPath.item].title 行中得到“致命错误:索引超出范围:文件 Swift/ContiguousArrayBuffer.swift,第 444 行”
  • @dohoudjann - 嗯...您是否按原样运行代码?或者你做了一些修改?我没有收到那个错误。
  • 不是原样,我将 vc.titleString = self.myData[theIndexPath.item].titlevc.descriptionString = self.myData[theIndexPath.item].tasks 更改为 cell.titleLabel.text = myData[indexPath.item].descriptionTitle cell.tableData = myData[indexPath.item].tasks 以符合我的数据结构 *Add
  • @dohoudjann -- 如果代码按原样运行,但在更改以符合数据时出现错误,则需要调试您的数据
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-11-28
  • 2011-01-03
  • 1970-01-01
相关资源
最近更新 更多