我正在尝试使用委托模式为 collectionView 的高度变化设置动画。触发此更改的按钮位于标题中。但是,当我按下按钮时,不仅高度不会改变,而且还会因错误而崩溃
'NSInvalidArgumentException',原因:'-[UIButton 长度]:无法识别的选择器发送到实例 0x12f345b50'
我觉得我做的一切都是正确的,但是当我点击按钮时它总是崩溃。有没有人看到任何错误,无论如何我可以按照我想要的方式为单元格的高度变化设置动画。这是单元类以及协议和委托。
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)
}
}
这是最终应该通过委托函数处理扩展的视图。
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
在这里为集合决定了高度
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