ios - 使用委托(delegate)模式的选择器无效

标签 ios swift delegates uicollectionviewdelegate

我正在尝试使用委托(delegate)模式为 collectionView 的高度变化设置动画。触发此更改的按钮位于标题中。然而,当我按下按钮时,不仅高度没有改变,而且还会因错误而崩溃

'NSInvalidArgumentException', reason: '-[UIButton length]: unrecognized selector sent to instance 0x12f345b50'

我觉得我做的一切都是对的,但是当我点击按钮时它总是崩溃。有没有人看到任何错误,无论如何我可以按照我想要的方式为单元格的高度变化设置动画。这是单元类以及协议(protocol)和委托(delegate)。

import Foundation
import UIKit

protocol ExpandedCellDelegate:NSObjectProtocol{
    func viewEventsButtonTapped(indexPath:IndexPath)
}
class EventCollectionCell:UICollectionViewCell {
    var headerID = "headerID"
    weak var delegateExpand:ExpandedCellDelegate?
    public var indexPath:IndexPath!
    var eventArray = [EventDetails](){
        didSet{
            self.eventCollectionView.reloadData()
        }
    }

    var enentDetails:Friend?{
        didSet{

            var name = "N/A"
            var total = 0
            seperator.isHidden = true
            if let value = enentDetails?.friendName{
                name = value
            }
            if let value = enentDetails?.events{
                total = value.count
                self.eventArray = value
                seperator.isHidden = false
            }
            if let value = enentDetails?.imageUrl{
                profileImageView.loadImage(urlString: value)
            }else{
                profileImageView.image =  imageLiteral(resourceName: "Tokyo")
            }

            self.eventCollectionView.reloadData()
            setLabel(name: name, totalEvents: total)
        }
    }

    let container:UIView={
        let view = UIView()
        view.translatesAutoresizingMaskIntoConstraints = false
        view.layer.cornerRadius = 16
        view.layer.borderColor = UIColor.lightGray.cgColor
        view.layer.borderWidth = 0.3
        return view
    }()
    //profile image view for the user
    var profileImageView:CustomImageView={
        let iv = CustomImageView()
        iv.layer.masksToBounds = true
        iv.layer.borderColor = UIColor.lightGray.cgColor
        iv.layer.borderWidth = 0.3
        iv.translatesAutoresizingMaskIntoConstraints = false
        return iv
    }()
    //will show the name of the user as well as the total number of events he is attending
    let labelNameAndTotalEvents:UILabel={
        let label = UILabel()
        label.textColor = .black
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        return label
    }()

    let seperator:UIView={
        let view = UIView()
        view.backgroundColor = .lightGray
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()

    //collectionview that contains all of the events a specific user will be attensing
    lazy var eventCollectionView:UICollectionView={
        let flow = UICollectionViewFlowLayout()
        flow.scrollDirection = .vertical
        let spacingbw:CGFloat = 5
        flow.minimumLineSpacing = 0
        flow.minimumInteritemSpacing = 0
        let cv = UICollectionView(frame: .zero, collectionViewLayout: flow)
        //will register the eventdetailcell
        cv.translatesAutoresizingMaskIntoConstraints = false
        cv.backgroundColor = .white
        cv.register(EventDetailsCell.self, forCellWithReuseIdentifier: "eventDetails")
        cv.register(FriendsEventsViewHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: headerID)
        cv.delegate = self
        cv.dataSource = self
        cv.backgroundColor = .blue
        cv.contentInset = UIEdgeInsetsMake(spacingbw, 0, spacingbw, 0)
        cv.showsVerticalScrollIndicator = false
        cv.bounces = false
        return cv
    }()
    override init(frame: CGRect) {
        super.init(frame: frame)
        self.setUpCell()
    }
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }



    func setLabel(name:String,totalEvents:Int){
        let mainString = NSMutableAttributedString()

        let attString = NSAttributedString(string:name+"\n" , attributes: [NSAttributedStringKey.foregroundColor:UIColor.black,NSAttributedStringKey.font:UIFont.systemFont(ofSize: 14)])
        mainString.append(attString)

        let attString2 = NSAttributedString(string:totalEvents == 0 ? "No events" : "\(totalEvents) \(totalEvents == 1 ? "Event" : "Events")" , attributes: [NSAttributedStringKey.foregroundColor:UIColor.darkGray,NSAttributedStringKey.font:UIFont.italicSystemFont(ofSize: 12)])
        mainString.append(attString2)
        labelNameAndTotalEvents.attributedText = mainString

    }
}

//extension that handles creation of the events detail cells as well as the eventcollectionview
//notice the delegate methods

//- Mark EventCollectionView DataSource
extension EventCollectionCell:UICollectionViewDataSource{
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        return eventArray.count
    }
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
        let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: headerID, for: indexPath) as! FriendsEventsViewHeader

            header.viewEventsButton.addTarget(self, action: #selector(viewEventsButtonTapped), for: .touchUpInside)
        return header
    }

    @objc func viewEventsButtonTapped(indexPath:IndexPath){
        print("View events button touched")
        if let delegate = self.delegateExpand{
            delegate.viewEventsButtonTapped(indexPath: indexPath)
        }

    }

    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier:"eventDetails" , for: indexPath) as! EventDetailsCell
        cell.details = eventArray[indexPath.item]
        cell.backgroundColor = .yellow
        cell.seperator1.isHidden = indexPath.item == eventArray.count-1
        return cell
    }
}

//- Mark EventCollectionView Delegate
extension EventCollectionCell:UICollectionViewDelegateFlowLayout{
    //size for each indvidual cell
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 50)
    }
    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
        return CGSize(width: collectionView.frame.width, height: 40)
    }

}

这是最终应该通过委托(delegate)函数处理扩展的 View 。

import UIKit
import Firebase

class FriendsEventsView: UIViewController,UICollectionViewDelegate,UICollectionViewDataSource,UICollectionViewDelegateFlowLayout {
    var friends = [Friend]()
    var followingUsers = [String]()
    var notExpandedHeight : CGFloat = 100
    var expandedHeight : CGFloat?
    var isExpanded = [Bool]()

    //so this is the main collectonview that encompasses the entire view
    //this entire view has eventcollectionCell's in it which in itself contain a collectionview which also contains cells
    //so I ultimately want to shrink the eventCollectionView
    lazy var mainCollectionView:UICollectionView={
        // the flow layout which is needed when you create any collection view
        let flow = UICollectionViewFlowLayout()
        //setting the scroll direction
        flow.scrollDirection = .vertical
        //setting space between elements
        let spacingbw:CGFloat = 5
        flow.minimumLineSpacing = spacingbw
        flow.minimumInteritemSpacing = 0
        //actually creating collectionview
        let cv = UICollectionView(frame: .zero, collectionViewLayout: flow)
        //register a cell for that collectionview
        cv.register(EventCollectionCell.self, forCellWithReuseIdentifier: "events")
        cv.translatesAutoresizingMaskIntoConstraints = false
        //changing background color
        cv.backgroundColor = .red
        //sets the delegate of the collectionView to self. By doing this all messages in regards to the  collectionView will be sent to the collectionView or you.
        //"Delegates send messages"
        cv.delegate = self
        //sets the datsource of the collectionView to you so you can control where the data gets pulled from
        cv.dataSource = self
        //sets positon of collectionview in regards to the regular view
        cv.contentInset = UIEdgeInsetsMake(spacingbw, 0, spacingbw, 0)
        return cv

    }()


    //label that will be displayed if there are no events
    let labelNotEvents:UILabel={
        let label = UILabel()
        label.textColor = .lightGray
        label.translatesAutoresizingMaskIntoConstraints = false
        label.numberOfLines = 0
        label.font = UIFont.italicSystemFont(ofSize: 14)
        label.text = "No events found"
        label.isHidden = true
        return label
    }()


    override func viewDidLoad() {
        super.viewDidLoad()
        //will set up all the views in the screen
        self.setUpViews()
        self.navigationItem.rightBarButtonItem = UIBarButtonItem(image:  imageLiteral(resourceName: "close_black").withRenderingMode(.alwaysOriginal), style: .done, target: self, action: #selector(self.goBack))
    }

    func setUpViews(){
        //well set the navbar title to Friends Events
        self.title = "Friends Events"
        view.backgroundColor = .white

        //adds the main collection view to the view and adds proper constraints for positioning
        view.addSubview(mainCollectionView)
        mainCollectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 0).isActive = true
        mainCollectionView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 0).isActive = true
        mainCollectionView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
        mainCollectionView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
        //adds the label to alert someone that there are no events to the collectionview and adds proper constrains for positioning
        mainCollectionView.addSubview(labelNotEvents)
        labelNotEvents.centerYAnchor.constraint(equalTo: mainCollectionView.centerYAnchor, constant: 0).isActive = true
        labelNotEvents.centerXAnchor.constraint(equalTo: mainCollectionView.centerXAnchor, constant: 0).isActive = true
        //will fetch events from server
        self.fetchEventsFromServer()

    }



    // MARK: CollectionView Datasource for maincollection view
//will let us know how many eventCollectionCells tht contain collectionViews will be displayed 
    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
        print(friends.count)
        isExpanded = Array(repeating: false, count: friends.count)
        return friends.count
    }
    //will control the size of the eventCollectionCells that contain collectionViews

高度由collectionVIew here决定

    func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
        let event = friends[indexPath.item]
        if let count = event.events?.count,count != 0{
            notExpandedHeight += (CGFloat(count*40)+10)
        }
        self.expandedHeight = notExpandedHeight

        if isExpanded[indexPath.row] == true{
            return CGSize(width: collectionView.frame.width, height: expandedHeight!)
        }else{
            return CGSize(width: collectionView.frame.width, height: 100)
        }
    }
    //will do the job of effieicently creating cells for the eventcollectioncell that contain eventCollectionViews using the dequeReusableCells function
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "events", for: indexPath) as! EventCollectionCell
        cell.backgroundColor = UIColor.orange
        cell.indexPath = indexPath
        cell.delegateExpand = self
        cell.enentDetails = friends[indexPath.item]
        return cell
    }
}

extension FriendsEventsView:ExpandedCellDelegate{
    func viewEventsButtonTapped(indexPath:IndexPath) {
        isExpanded[indexPath.row] = !isExpanded[indexPath.row]
        print(indexPath)
        UIView.animate(withDuration: 0.8, delay: 0.0, usingSpringWithDamping: 0.9, initialSpringVelocity: 0.9, options: UIViewAnimationOptions.curveEaseInOut, animations: {
            self.mainCollectionView.reloadItems(at: [indexPath])
        }, completion: { success in
            print("success")
        })
    }
}

我用这篇文章作为引用来实现 Expandable UICollectionViewCell

最佳答案

这是一个很常见的错误。

在目标/操作选择器中传递的参数总是触发操作的受影响的 UI 元素,在您的例子中是按钮。

您不能传递任意对象,例如 indexPath,因为 addTarget 方法中没有参数来指定该任意对象。

你必须声明选择器

@objc func viewEventsButtonTapped(_ sender: UIButton) {

或不带参数

@objc func viewEventsButtonTapped() {

UIControl 提供了第三种语法

@objc func viewEventsButtonTapped(_ sender: UIButton, withEvent event: UIEvent?) {

不支持任何其他语法。

关于ios - 使用委托(delegate)模式的选择器无效,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48739775/

相关文章:

ios - 如何让 uitableview 填满我所有的 View ?

java - Appium无法在模拟器中安装ipa文件

iphone - 可达性中的 UIAlertView 异常

ios - 如何让WKWebView在后台显示

ios - 是否需要保留数据源的 Controller 和 uiPickerview 的委托(delegate)?

ios - 将 NSPredicate 设置为 NSFetchRequest 后 executeFetchRequest 崩溃

swift - 使用 Where 语句从 Firebase 计算 Swift 中的记录

SwiftUI:如何更改 NavigationView.toolbar 背景颜色

objective-c - id 类型实例变量向实例方法发送消息

swift - 我的委托(delegate)出了什么问题?