【问题标题】:How can I access UISegmentedControl in header of UICollectionViewController?如何在 UICollectionViewController 的标题中访问 UISegmentedControl?
【发布时间】:2016-07-10 08:22:00
【问题描述】:

所以这个问题相当简单。我有一个UICollectionViewController(MyProfile.swift) 和一个标题部分(MyProfileHeader.swift)。在后者中,我有一个 UISegmentedControl 来返回不同数量的项目和 collection view cells 中的项目(我不想在 UICollectionViewController 中初始化后者类的实例)。这是我的MyProfile.swift class 代码。我尝试在viewForSupplementaryElementOfKind 方法中添加一个目标以返回不同的查询(这可行),但我最终必须访问numberOfItemsInSectioncellForItemAtIndexPath 方法中的分段控件。 "testObjects""writeObjects"array 值,通过viewForSupplementaryElementOfKind 中的addTarget 方法查询。我设置了indexPath,但它设置为returnserror,原因很明显......我怎样才能访问分段控制?

    override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {

    var numberOfItems: Int? = 0

    let indexPath = NSIndexPath(forItem: 0, inSection: 0)

    let header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "header", forIndexPath: indexPath) as! MyProfileHeader


    if header.userContent.selectedSegmentIndex == 1 {
        numberOfItems = textObjects.count
    } else if header.userContent.selectedSegmentIndex == 2 {
        numberOfItems = 0
    } else {
        numberOfItems = photoObjects.count
    }



    print("F: \(numberOfItems!)")
    return numberOfItems!
}

【问题讨论】:

    标签: ios swift ios7 swift2


    【解决方案1】:

    -1st 创建一个UICollectionReusableView 子类并将其命名为SegmentedHeader

    -2nd 在 SegmentedHeader 类中添加一个协议来跟踪选择了哪个段。当在 collectionView 的标头中选择一个段时,协议/委托将传递该段的值

    -3rd 确保你设置了代理weak var delegate: SegmentedHeaderDelegate?

    -4 在以编程方式创建 segmentedControl 时添加一个名为 selectedIndex(_ sender: UISegmentedControl) 的目标。当一个段被按下时,你将该段的值传递给协议 trackSelectedIndex() 函数

    protocol SegmentedHeaderDelegate: class {
        func trackSelectedIndex(_ theSelectedIndex: Int)
    }
    
    class SegmentedHeader: UICollectionReusableView {
    
        //MARK:- Programmatic Objects
        let segmentedControl: UISegmentedControl = {
            let segmentedControl = UISegmentedControl(items: ["Zero", "One", "Two"])
            segmentedControl.translatesAutoresizingMaskIntoConstraints = false
            segmentedControl.tintColor = UIColor.red
            segmentedControl.backgroundColor = .white
            segmentedControl.isHighlighted = true
            segmentedControl.addTarget(self, action: #selector(selectedIndex(_:)), for: .valueChanged)
            return segmentedControl
        }()
    
        //MARK:- Class Property
        weak var delegate: SegmentedHeaderDelegate?
    
        //MARK:- Init Frame
        override init(frame: CGRect) {
            super.init(frame: frame)
    
            backgroundColor = .white
            setupAnchors()
        }
    
        //MARK:- TargetAction
        @objc func selectedIndex(_ sender: UISegmentedControl){
    
            let index = sender.selectedSegmentIndex
    
            switch index {
    
            case 0: // this means the first segment was chosen
                delegate?.trackSelectedIndex(0)
                break
    
            case 1: // this means the middle segment was chosen
                delegate?.trackSelectedIndex(1)
                break
    
            case 2: // this means the last segment was chosen
                delegate?.trackSelectedIndex(2)
                break
    
            default:
                break
            }
        }
    
        fileprivate func setupAnchors(){
    
            addSubview(segmentedControl)
    
            segmentedControl.leftAnchor.constraint(equalTo: self.leftAnchor, constant: 0).isActive = true
            segmentedControl.rightAnchor.constraint(equalTo: self.rightAnchor, constant: 0).isActive = true
            segmentedControl.topAnchor.constraint(equalTo: self.topAnchor, constant: 0).isActive = true
            segmentedControl.bottomAnchor.constraint(equalTo: self.bottomAnchor, constant: 0).isActive = true
        }
    
        required init?(coder aDecoder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    

    在具有 UICollectionViewController 的类中:

    重要提示 - 确保在 viewForSupplementaryElementOfKind 中设置委托,否则这些都不起作用

    // MAKE SURE YOU INCLUDE THE SegmentedHeaderDelegate so the class conforms to it
    class ViewController: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout, SegmentedHeaderDelegate{
    
    // add a class property for the header identifier
    let segmentedHeaderIdentifier = "segmentedHeader"
    
    // add a class property to keep track of which segment was selected. This gets set inside the tracktSelectedIndex() function. You will need this for cellForRowAtIndexPath so you can show whatever needs to be shown for each segment
    var selectedSegment: Int?
    
    // register the SegmentedHeader with the collectionView
    collectionView.register(SegmentedHeader.self, forSupplementaryViewOfKind: UICollectionElementKindSectionHeader, withReuseIdentifier: segmentedHeaderIdentifier)
    
    // inside the collectionView's delegate below add the header
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
    
            var header: UICollectionReusableView?
    
            if kind == UICollectionElementKindSectionHeader{
    
                let segmentedHeader = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: segmentedHeaderIdentifier, for: indexPath) as! SegmentedHeader
    
                // IMPORTANT >>>>MAKE SURE YOU SET THE DELEGATE or NONE OF THIS WILL WORK<<<<
                segmentedHeader.delegate = self
    
                // when the scene first appears there won't be any segments chosen so if you want a default one to show until the user picks one then set it here
                // for eg. when the scene first appears the last segment will show
                segmentedHeader.segmentedControl.selectedSegmentIndex = 2
    
                header = segmentedHeader
            }
    
            return header!
        }
    
    // inside cellForRowAtIndexPath check the selectedSegmented class property to find out which segment was chosen
    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    
            let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TheCell, for: indexPath) as! TheCell
    
            // selectedSegment is the class property that gets set inside trackSelectedIndex()
            switch selectedSegment {
    
                case 0:
                // have the cell display something for the first segment
                break
    
                case 1:
                // have the cell display something for the middle segment
                break
    
                case 2:
                // have the cell display something for the last segment
                break
    
                default:
                break
            }
    
            return cell
    }
    
    
    // whenever a segment is selected, this delegate function  will get passed the segment's index. It runs a switch statement on theSelectedIndex argument/parameter. Based on that result it will set the selectedIndex class property to match the value from theSelectedIndex argument/parameter
    func trackSelectedIndex(_ theSelectedIndex: Int) {
    
            switch theSelectedIndex {
    
            case 0: // this means the first segment was chosen
                // set the selectedSegment class property so you can use it inside cellForRowAtIndexPath
                selectedSegment = 0
                print("the selected segment is: \(theSelectedIndex)")
                break
    
            case 1: // this means the middle segment was chosen
                selectedSegment = 1
                print("the selected segment is: \(theSelectedIndex)")
                break
    
            case 2: // this means the last segment was chosen
                selectedSegment = 2
                print("the selected segment is: \(theSelectedIndex)")
                break
    
            default:
                break
            }
    }
    

    【讨论】:

    • 我想根据switch语句改变cellForItemAt中单元格的类型。在每种情况下,我都试图让 cell = 出列某个 Cell。即案例0:TypeACell,案例1:TypeBCell;但是,它不起作用,因为我没有从 switch 语句中得到任何回报。我该如何解决这个问题?
    • @geistmate 您需要将 switch 语句中的每个单元格出列,并确保在同一个 switch 语句中返回相同的单元格。例如案例 0:让 cell0 = cv.dequ... return cell0;案例 1:让 cell1 = cv.dequ... 返回 cell1。您必须为默认情况添加一些单元格
    • 我已经开始工作了。非常感谢您的快速回复。最后一件事很抱歉,如果我想改变单元格的高度,我还必须将 switch 语句放在 sizeForItemAt 中?
    • @geistmate 是的,你必须做同样的事情,并使用 switch 语句设置大小以匹配单元格,就像你在 cellForItem 中所做的那样
    • 现在一切正常。我感谢所有的帮助,谢谢。 :)
    【解决方案2】:

    当在viewForSupplementaryElementOfKind 中为集合视图检索标题时,您可以在MyProfile 中存储对它的弱引用。

    class MyProfile: UICollectionViewController {
    ...
    ...
    weak var header: MyProfileHeader?
    ...    
    ...
    func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
        header = collectionView.dequeueReusableSupplementaryViewOfKind(UICollectionElementKindSectionHeader, withReuseIdentifier: "header", forIndexPath: indexPath) as! MyProfileHeader
    
        return header
    }
    

    然后,您可以从 UICollectionViewController 中的任何其他功能访问它。

    请注意,numberOfItemsInSectioncellForItemAtIndexPath 可以在 viewForSupplementaryElementOfKind 中创建标头之前调用,因此当您在 numberOfItemsInSectioncellForItemAtIndexPath 或其他任何地方访问它时,您应该检查 null 然后假设分段控件处于默认值(因为这是第一次显示视图)。类似的东西

    let selectedSegmentIndex = header?.userContent.selectedSegmentIndex ?? 0 //0 is the default value here
    

    【讨论】:

    • 我尝试了你的建议,但我得到了错误:“unexpectedly found nil while unwrapping an optional value”
    • 也许您在访问标头时得到一个空值,因为它尚未创建。我已经编辑了我的答案以适应这一点。
    • 对不起@davedavedave,我不知道我会在哪里添加该代码...是在 viewDidLoad 还是...?另外,非常感谢哈哈
    • 我试过在viewDidLoad中实例化它,但还是不行:header = MyProfileHeader()
    • 标头仅在 viewForSupplementaryElementOfKind 函数中实例化 - 当您想从控件访问选定段时,您使用 let selectedsegmentIndex... 行。因此,在您的情况下,可能是 numberOfItemsInSection 和 cellForItemAtIndexPath 函数。
    猜你喜欢
    • 2013-10-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多