0

I read all questions on stackoverflow but none of them could solve my problem. I created a UICollectionView and and anchored a searchBar inside of the navigationBar without using Storyboards!

   class UserSearchController: UICollectionViewController ,.. {..
    lazy var  searchBar: UISearchBar = {
    let sb = UISearchBar()
    sb.placeholder = "Enter username"
    sb.barTintColor = UIColor.gray
    UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
    sb.delegate = self
    sb.showsCancelButton = true
    return sb
}()  .... 

  // adding the searchBar to the collectionView as subView and      
    anchoring it  to the navigationBar

enter image description here

The searchbar is shown inside of the navigationBar and everything works fine. The problem occurs when I try to add a scopebar to my searchbar. I added the following properties to my searchbar

      sb.showsScopeBar = true
      sb.scopeButtonTitles = ["Username", "Hashtag"]

The scopeBar is hide behind the navigationBar and the navBar is not automatically increasing its height. You just see a gray background.

enter image description here

Adding my code to a simple UiViewController everything works fine

This code works inside a normal VIewController where the searchBar is added as subView and anchored to the view.

    lazy var  searchBar: UISearchBar = {

    let sb = UISearchBar()
    sb.placeholder = "Enter username"
    sb.barTintColor = UIColor.gray
    UITextField.appearance(whenContainedInInstancesOf: [UISearchBar.self]).backgroundColor = UIColor.rgb(red: 230, green: 230, blue: 230)
    sb.delegate = self
    sb.scopeButtonTitles = ["Username", "Hashtag"]
    sb.tintColor = UIColor.black
    sb.barTintColor = UIColor.lightGray


    return sb
}()

public func searchBarShouldBeginEditing(_ searchBar: UISearchBar) -> Bool {
    searchBar.showsScopeBar = true
    searchBar.sizeToFit()
    searchBar.setShowsCancelButton(true, animated: true)

    return true
}



func searchBarCancelButtonClicked(_ searchBar: UISearchBar) {
    searchBar.showsScopeBar = false
    searchBar.endEditing(true)
}

My searchBar looks like the following. If you start typing the scopeBar appears and if you click cancel it disappears. I want the same but only inside of the navBar :

enter image description here enter image description here

How is it possible to add a searchBar with scopeBar inside of the navigationBar without Storyboards. Isn't there any easy way. Please can you refer to my code and use my code to show me how this works. I also tried to increase the size of the navigationBar but it should work automatically and I don't know if this is the right approach.

Developer Omari
  • 434
  • 5
  • 14
  • you could try ditching the `scopeBar` and adding a `UISegmentController` below your search bar instead to filter your searches based on the selected segment – Garret Kaye May 24 '17 at 17:20
  • I created a header for my collectionView and added the segmented Control inside of my header. The problem was that I couldn't change the content of my collectionView based on the selected segment. I already asked that question and they recommend using the scopeBar property from the searchBar and now you recommend using segmentedControl :) – Developer Omari May 24 '17 at 17:28
  • https://stackoverflow.com/questions/44154384/changing-content-of-uicollectionview-with-segmented-control-in-the-header-in-swi – Developer Omari May 24 '17 at 17:28
  • Oh I see sorry about that I just thought I would offer a different alternative. Could you please elaborate on why you cant change search content based on the selected segment? Ill see if I can help – Garret Kaye May 24 '17 at 17:47
  • Thanks for your help. The problem is described here in the link above. Can you please answer to that question. – Developer Omari May 24 '17 at 18:18
  • See answer below – Garret Kaye May 24 '17 at 21:01

1 Answers1

0

I think this is the sort of implementation you are looking for:

Your segment controller

// ( Declare Globaly )
var yourSegmentController = UISegmentedControl()
yourSegmentController.addTarget(self, action: #selector(ParentClass.filterChanged(_:)), for: .touchUpInside)

Then you will have to register your custom UICollectionViewCell classes with your UICollectionView. Do this in your viewDidLoad or whatever initializer method is used in the module you have the UICollectionView inside of:

self.yourCollectionView.register(YourCustomUserCellClassHere.self, forCellWithReuseIdentifier: NSStringFromClass(YourCustomUserCellClassHere))

self.yourCollectionView.register(YourCustomHashtagCellClassHere.self, forCellWithReuseIdentifier: NSStringFromClass(YourCustomHashtagCellClassHere))

Lastly, in your cellForItemAt delegate method, do the following to see which filter is selected and return the cells accordingly:

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {

    var cellToReturn = UICollectionViewCell()

    // USERS
    if yourSegmentController.selectedSegmentIndex == 0 {

        let customUserCell = self.yourCollectionView.dequeueReusableCell( withReuseIdentifier: NSStringFromClass(YourCustomUserCellClassHere), for: indexPath) as? YourCustomUserCellClassHere

        // Do what you want with your custom cell here...


        cellToReturn = customUserCell
    }
    // HASHTAGS
    else if yourSegmentController.selectedSegmentIndex == 1 {

        let customHashtagCell = self.yourCollectionView.dequeueReusableCell( withReuseIdentifier: NSStringFromClass(YourCustomHashtagCellClassHere), for: indexPath) as? YourCustomHashtagCellClassHere

        // Do what you want with your custom cell here...


        cellToReturn = customHashtagCell

    }

    return cellToReturn

}

This will check to see what filter is selected and then return the cell that suits the filter.

Also keep in mind every time the user changes your segment filter you will have to refresh the UICollectionView.

Refresh it like this:

// Put this method at the same level where your cellForItemAt function is but not inside

func filterChanged (_ sender:UISegmentedControl) {
    self.yourCollectionView.reloadData()
} 
Garret Kaye
  • 2,412
  • 4
  • 21
  • 45
  • Thank you. I tried it exactly the way you described it before. The problem is that the content of the collectionVIew Is not changing because there is no target action method for the segmented control. I don't have any idea where you could put that function. I hope you can help me because this problem makes me headache since HOURS. – Developer Omari May 24 '17 at 21:23
  • we should say something like sc.addTarget(self, action: #selector(segmentedChange), for: .valueChanged) and then execute collectionView.reloadData but no idea where to put this code in our current situation – Developer Omari May 24 '17 at 21:27
  • But how can we reload the data of the collectionView from the header class. I created the segmentedControl as global Variable inside of the headerClass – Developer Omari May 24 '17 at 21:28
  • Read updated answer let me know if you still have any questions – Garret Kaye May 24 '17 at 21:33
  • Thanks Garret. Following problem left when adding the target to my global defined segmentedControl called sc: sc.addTarget(self, action: #selector(#selector(UserSearchController.filterChanged(_:))), for: .touchUpInside) --> it says use of unresolved identifier – Developer Omari May 24 '17 at 21:36
  • Okay so is all well? – Garret Kaye May 24 '17 at 21:37
  • I think the problem is that we are using self and the segmentedControl is created global outside of my headerClass at the top of the file – Developer Omari May 24 '17 at 21:43
  • oh change it to this: `#selector(UserSearchController.filterChanged(_:))` there is only supposed to be one `#selector` element. My bad, typo. – Garret Kaye May 24 '17 at 21:43
  • The compiler still has a problem with the self keyword its says use of unresolved identifier I think the problem is that we are using self and the segmentedControl is created global outside of my headerClass at the top of the file. sc.addTarget(self, action: #selector(UserSearchController.filterChanged(_:)), for: .touchUpInside) – Developer Omari May 24 '17 at 21:46
  • Yeah your going to have to figure that one out. I do not know how your program is aranged. In any case you are going to need access to your segment controller from your `cellForItemAt` delegate method. – Garret Kaye May 24 '17 at 21:57
  • Please help me Garret. I can access the segment controller from cellForItemAt delegate method that works fine. The problem is that the content doesn't change. I have like mentioned above two CellClasses and one HeaderClass where my segmentedControl is defined globally. Can I use as target: UserSearchController() instead self . Please – Developer Omari May 24 '17 at 21:58
  • Is the filterChanged method being called – Garret Kaye May 24 '17 at 22:02
  • No its not being called. and still the problem with the self keyword. – Developer Omari May 24 '17 at 22:03
  • UserSearchController is my CollectionView that is registered with two custom UICollectionViewCells called UserSearchCell and HashTagSearchCell to render the cells and a UserSearchHeader Class to render the Header The segmentedControl is part of the header. Therefore Inside the UserSearchHeader my segmentedControl is defined global and add as subview to the headers View. – Developer Omari May 24 '17 at 22:10
  • Okay create an instance of `UserSearchHeader` in your `UserSearchController` like this: `let searchHeader = UserSearchHeader()` then create an instance of your segment controller like this `let segmentController = searchHeader.yourSegmentController` then add the target to that segment controller inside your `UserSearchController` as well as `filterChanged` – Garret Kaye May 24 '17 at 22:19
  • When creating an instance of my segment controller inside UserSearchController class like let segmentController = searchHeader.segmentedControl following error message occurs: Cannot use instance member searchHeader within property initializer; property initializer run before self available. – Developer Omari May 24 '17 at 22:27
  • yes but make both of them global vars. Outside of viewDidLoad (Global): `var searchHeader:UserSearchHeader!` and `var segmentController:UISegmentControl!`. Inside of viewDidLoad: `searchHeader = UserSearchHeader()` and `segmentController = searchHeader.yourSegmentController` – Garret Kaye May 24 '17 at 22:34
  • Did everything like you described but the filterChange method is not executed – Developer Omari May 24 '17 at 22:42
  • Garret please help me I still having this issues and don't find any solution – Developer Omari May 25 '17 at 10:07
  • I see references to `.touchUpInside` at 3 different places in the above discussion. Please note that `UISegmentedControl` instances do NOT generate `.touchUpInside` events; they generate `.valueChanged` events instead when the selected segment changes. If you are only checking for `.touchUpInside` events in your target-action set-up, your action method will never be called. – inwit Dec 13 '17 at 19:19