So this problem is fairly straightforward. I have a UICollectionViewController
(MyProfile.swift)
with a header section (MyProfileHeader.swift)
. Within the latter, I have a UISegmentedControl
to return different numbers of items AND items in the collection view cells
(I don't want to initialize an instance of the latter's class within the UICollectionViewController
). This is my code for MyProfile.swift class
. I tried adding a target in the viewForSupplementaryElementOfKind
method to return different queries (which works), but I ultimately have to access the segmented control within the numberOfItemsInSection
and cellForItemAtIndexPath
methods. The "testObjects"
and "writeObjects"
are array
values that are queried via the addTarget
method in the viewForSupplementaryElementOfKind
. I set the indexPath
but it returns
an error
for obvious reasons... How can I access segmented control?
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!
}
-1st create a UICollectionReusableView
subclass and name it SegmentedHeader
-2nd Inside the SegmentedHeader class add a protocol to keep track of which segment was selected. When a segment is selected inside the collectionView's header the protocol/delegate will get passed the value of that segment
-3rd make sure you set the delegate weak var delegate: SegmentedHeaderDelegate?
-4th when programmatically creating the segmentedControl
add a target named selectedIndex(_ sender: UISegmentedControl) . When a segment is pressed, you pass the value of that segment to the protocols trackSelectedIndex() function
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")
}
}
Inside the class that has the UICollectionViewController:
IMPORTANT - Make sure you set the delegate inside viewForSupplementaryElementOfKind or none of this will work
// 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
}
}
When the header is retrieved for the collection view in viewForSupplementaryElementOfKind
, you can store a weak reference to it in 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
}
You can then access this from any other function in your UICollectionViewController
.
Note that numberOfItemsInSection
and cellForItemAtIndexPath
can be called before the header has been created in viewForSupplementaryElementOfKind
, so when you access it in numberOfItemsInSection
, cellForItemAtIndexPath
, or anywhere else you should check for null and then assume that the segmented control is on the default value (as it will be since this is the first time the view is being displayed). Something like
let selectedSegmentIndex = header?.userContent.selectedSegmentIndex ?? 0 //0 is the default value here
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.