I am trying to update the content of the cells in a UICollectionView based on what the user inputs into a textField in the header. I have looked at other similar questions here but none of their answers have seemed to work.I even tried implementing an extension to the UICollectionView which goes through each cell in the section trying to reload its data but that leads to a crash as the number of cells are changing after the user enters into the textField
Function I attempted to solve the problem, I have only one section so I hard coded in that section
@objc func textFieldDidChange(){
guard(!(feedSearchBar.text?.isEmpty)!) else{
VC.currentTagsArray = VC.genericTagsArray
VC.feedScreenCollectionView.reloadData()
return
}
VC.currentTagsArray = VC.genericTagsArray.filter({letter -> Bool in
if feedSearchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: feedSearchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(feedSearchBar.text!.lowercased())
})
if VC.currentTagsArray.isEmpty{
VC.currentTagsArray.insert(feedSearchBar.text!, at: 0)
}
VC.feedScreenCollectionView.reloadItems(inSection: 0)
}
extension UICollectionView{
func reloadItems(inSection section:Int = 0) {
print("made it to reload")
for i in 0..<self.numberOfItems(inSection: section){
self.reloadItems(at: [IndexPath(item: i, section: section)])
}
}
}
Here is the code in its entirety from the project
import UIKit
class ViewController: UIViewController,UICollectionViewDelegateFlowLayout,UICollectionViewDelegate,UICollectionViewDataSource,printDelegateWorkedDelegate,updateCollectionView{
func updateCollectionView() {
self.feedScreenCollectionView.reloadData()
}
func printDelegateWorkedDelegate() {
print("The delegate worked")
}
var genericTagsArray:[String] = ["tag1","tag2","tag3","tag4","tag5","tag6","tag7","tag8","tag9","tag10","tag11","tag12","A","B","C","D","E","F","G","Ab","Abc","za","tag1","tag2","tag3","tag4","tag5","tag6","tag7","tag8","tag9","tag10","tag11","tag12","A","B","C","D","E","F","G","Ab","Abc","za"]
var currentTagsArray:[String] = [String]()
var tagsSelected:[String] = [String]()
let keyboardSlider = KeyboardSlider()
var header:feedViewHeader = feedViewHeader()
@IBOutlet weak var feedScreenCollectionView: UICollectionView!
override func viewDidLoad() {
super.viewDidLoad()
keyboardSlider.subscribeToKeyboardNotifications(view: view)
currentTagsArray = genericTagsArray
let viewTapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(feedViewHeader.viewTapped(gestureRecognizer:)))
viewTapGestureRecognizer.cancelsTouchesInView = false
self.feedScreenCollectionView.addGestureRecognizer(viewTapGestureRecognizer)
feedScreenCollectionView.delegate = self
//
feedScreenCollectionView.dataSource = self
//
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 5, left: 1, bottom: 0, right: 1)
layout.minimumLineSpacing = 0
layout.headerReferenceSize = CGSize(width: 50, height: 75)
layout.sectionHeadersPinToVisibleBounds = true
feedScreenCollectionView.collectionViewLayout = layout
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize {
return CGSize(width: 50, height: 75)
}
func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
header = collectionView.dequeueReusableSupplementaryView(ofKind: UICollectionElementKindSectionHeader, withReuseIdentifier: "feedViewHeader", for: indexPath) as! feedViewHeader
header.VC = self
return header
}
//
//Data Source
func numberOfSections(in collectionView: UICollectionView) -> Int {
return 1
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return currentTagsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "feedViewCell", for: indexPath) as! feedViewCell
cell.feedImageView.backgroundColor = .blue
cell.feedImageView.clipsToBounds = true
cell.feedImageView.layer.cornerRadius = CGFloat((cell.feedImageView.frame.width)/5)
cell.feedLabel.text = currentTagsArray[indexPath.item]
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat{
return 0
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let collectionViewWidth = collectionView.bounds.width/4.0
let collectionViewHeight = collectionViewWidth
return CGSize(width: collectionViewWidth-4, height: collectionViewHeight+25)
}
var lastContentOffset:CGFloat = 0
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if self.lastContentOffset > self.feedScreenCollectionView.contentOffset.y && self.feedScreenCollectionView.contentOffset.y > 0 && self.feedScreenCollectionView.contentOffset.y < self.feedScreenCollectionView.frame.maxY {
self.lastContentOffset = scrollView.contentOffset.y
header.isHidden = false
}
else if (self.lastContentOffset < self.feedScreenCollectionView.contentOffset.y) && (self.feedScreenCollectionView.contentOffset.y < self.feedScreenCollectionView.frame.maxY) && (self.feedScreenCollectionView.contentOffset.y > 0) {
print("you scrolled down,content offSet: \(scrollView.contentOffset.y)->\(self.feedScreenCollectionView.contentOffset.y)")
header.isHidden = true
}
else{
self.lastContentOffset = scrollView.contentOffset.y
print("content offSet: \(scrollView.contentOffset.y)")
print("Nothing happened")
// self.headerDelegate?.hideHeaderView(hide: true)
}
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(ViewController.keyboardFrameChangeNotification(notification:)), name: NSNotification.Name.UIKeyboardWillChangeFrame, object: nil)
}
var offsetY:CGFloat = 0
@objc func keyboardFrameChangeNotification(notification: Notification) {
}
}
class feedViewCell:UICollectionViewCell{
@IBOutlet weak var feedImageView: UIImageView!
@IBOutlet weak var feedLabel: UILabel!
let keyboardSlider = KeyboardSlider()
override func awakeFromNib() {
super.awakeFromNib()
feedLabel.translatesAutoresizingMaskIntoConstraints = false
feedImageView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
feedImageView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
feedImageView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
feedImageView.bottomAnchor.constraint(equalTo: self.feedLabel.topAnchor).isActive = true
feedImageView.translatesAutoresizingMaskIntoConstraints = false
feedLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true
feedLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true
feedLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
feedLabel.topAnchor.constraint(equalTo: self.feedImageView.bottomAnchor).isActive = true
feedLabel.textAlignment = .center
}
}
class feedViewHeader:UICollectionReusableView,UITextFieldDelegate,UICollectionViewDelegate{
@IBOutlet weak var feedSearchBar: UITextField!
var delegateWorked:printDelegateWorkedDelegate?
var updateCV:updateCollectionView?
var VC:ViewController!
var collectionView:UICollectionView?
var stringToBeSet = "String to be set"
override func awakeFromNib() {
super.awakeFromNib()
feedSearchBar.delegate = self
feedSearchBar.autocorrectionType = .no
feedSearchBar.keyboardType = .default
feedSearchBar.addTarget(self, action: #selector(feedViewHeader.textFieldDidChange), for: .editingChanged)
self.feedSearchBar.borderStyle = .roundedRect
self.feedSearchBar.layer.borderColor = UIColor.black.cgColor
self.feedSearchBar.layer.borderWidth = 4
var searchBarHeight = self.feedSearchBar.bounds.height
self.feedSearchBar.placeholder = "Tap To Search"
self.feedSearchBar.returnKeyType = .search
self.feedSearchBar.rightViewMode = .always
}
@objc func viewTapped(gestureRecognizer:UIGestureRecognizer){
if feedSearchBar.isFirstResponder{
feedSearchBar.resignFirstResponder()
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
VC.feedScreenCollectionView.reloadData()
//VC.feedScreenCollectionView.reloadSections([0])
return true
}
/// Helper to dismiss keyboard
@objc func didStopEditing() {
}
func textFieldDidEndEditing(_ textField: UITextField) {
UIView.setAnimationCurve(UIViewAnimationCurve.easeInOut)
UIView.animate(withDuration: 0.2) {
self.VC.view.frame.origin.y = 0
}
}
@objc func textFieldDidChange(){
guard(!(feedSearchBar.text?.isEmpty)!) else{
VC.currentTagsArray = VC.genericTagsArray
VC.feedScreenCollectionView.reloadData()
return
}
VC.currentTagsArray = VC.genericTagsArray.filter({letter -> Bool in
if feedSearchBar.text!.count > letter.count{
return false
}
let stringRange = letter.index(letter.startIndex, offsetBy: feedSearchBar.text!.count)
let subword = letter[..<stringRange]
return subword.lowercased().contains(feedSearchBar.text!.lowercased())
})
if VC.currentTagsArray.isEmpty{
VC.currentTagsArray.insert(feedSearchBar.text!, at: 0)
}
VC.feedScreenCollectionView.reloadItems(inSection: 0)
}
}
extension Notification.Name{
static let showKeyboard = Notification.Name("showKeyboard")
}
class KeyboardSlider: NSObject {
// variables to hold and process information from the view using this class
weak var view: UIView?
@objc func keyboardWillShow(notification: NSNotification) {
// method to move keyboard up
// view?.frame.origin.y = 0 - getKeyboardHeight(notification as Notification)
print("made it to keyboard will show")
}
func getKeyboardHeight(_ notification:Notification) -> CGFloat {
// get exact height of keyboard on all devices and convert to float value to return for use
let userInfo = notification.userInfo
let keyboardSize = userInfo![UIKeyboardFrameEndUserInfoKey] as! NSValue
return keyboardSize.cgRectValue.height
}
func subscribeToKeyboardNotifications(view: UIView) {
// assigning view to class' counterpart
self.view = view
// when UIKeyboardWillShow do keyboardWillShow function
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
}
func unsubscribeFromKeyboardNotifications() {
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
}
}
class blankView:UICollectionReusableView{
}
extension UICollectionView{
func reloadItems(inSection section:Int = 0) {
print("made it to reload")
for i in 0..<self.numberOfItems(inSection: section){
self.reloadItems(at: [IndexPath(item: i, section: section)])
}
}
}