0

I'm trying to implement an UISearchBarController in UICollectionViewController, here are the codes:

    class FirstViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchResultsUpdating {

         var titles: [String] = []

         var card: [RecommendArticles]?

         var searchArr = [String](){
            didSet {
               self.collectionView?.reloadData()
        }
    }

        func updateSearchResults(for searchController: UISearchController) {

            guard let searchText = searchController.searchBar.text else {
                return
            }

            searchArr = titles.filter { (title) -> Bool in
                return title.contains(searchText)
            }


        }


        let searchController = UISearchController(searchResultsController: nil)

       override func viewDidLoad() {

        super.viewDidLoad()

        searchController.searchResultsUpdater = self
        searchController.dimsBackgroundDuringPresentation = false
        navigationItem.hidesSearchBarWhenScrolling = true
        self.navigationItem.searchController = searchController
 }


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

        if (searchController.isActive) {
            return searchArr.count
        } else {
             return self.counters
        }

    }

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

        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: verticalCellId, for: indexPath) as! VerticalCellId
        if (searchController.isActive) {
            cell.titleLabel.text = searchArr[indexPath.row]
            return cell
        } else {
            if let link = self.card?[indexPath.item]._imageURL {
                let url = URL(string: link)
                cell.photoImageView.kf.setImage(with: url)
            }

            if let title = self.card?[indexPath.item]._title {
                cell.titleLabel.text = title
                titles.append(title)
                print("\(titles)")
            }

            if let source = self.card?[indexPath.item]._source {
                cell.sourceLabel.text = source
            }

            return cell

        }




    }

Here are the errors locate:

                titles.append(title)
                print("\(titles)")

When I search a keyword, the filtered results are incorrect, because the collection view cell will change dynamically (I'm not sure if my expression is accurate).

But, if I set up the variable titles like this, it works perfectly:

var titles = ["Tom","Jack","Lily"]

But I have to retrieve the values of titles from internet. Any suggestions would be appreciated.

Here are the update question for creating an array after self.card:

override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(true)

        self.view.addSubview(activityView)
        activityView.hidesWhenStopped = true
        activityView.center = self.view.center
        activityView.startAnimating()

        let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
        dynamoDbObjectMapper.load(TheUserIds2018.self, hashKey: "TheUserIds2018", rangeKey: nil, completionHandler: { (objectModel: AWSDynamoDBObjectModel?, error: Error?) -> Void in
            if let error = error {
                print("Amazon DynamoDB Read Error: \(error)")
                return
            }

            DispatchQueue.main.async {
                if let count = objectModel?.dictionaryValue["_articleCounters"] {
                    self.counters = count as! Int
                    self.collectionView?.reloadData()
                    self.updateItems()
                    self.activityView.stopAnimating()
                }

            }

        })

    }



 func updateItems() {
        let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
        var tasksList = Array<AWSTask<AnyObject>>()
        for i in 1...self.counters {
            tasksList.append(dynamoDbObjectMapper.load(RecommendArticles.self, hashKey: "userId" + String(self.counters + 1 - i), rangeKey: nil))
        }
        AWSTask<AnyObject>.init(forCompletionOfAllTasksWithResults: tasksList).continueWith { (task) -> Any? in
            if let cards = task.result as? [RecommendArticles] {
                self.card = cards
                DispatchQueue.main.async {
                if let totalCounts = self.card?.count {
                    for item in 0...totalCounts - 1 {
                        if let title = self.card?[item]._title {
                            self.newTitle = title
                        }
                        self.titles.append(self.newTitle)
                        print("titles: \(self.titles)")
                    }
                }
        }


            } else if let error = task.error {
                print(error.localizedDescription)
            }

            return nil

        }

    }

1 Answer 1

2

This is not an issue with UISearchController . it is with your data. Collection view data source method -

cellForItemAtIndexPath works only when cell is presented in the view. So you can't keep the code for creating array of titles in the cellForItemAtIndexPath

Instead of separately keeping title strings in array for search you can directly filter self.card array with search text in title with below code

self.searchArr = self.card?.filter(NSPredicate(format:"_title CONTAINS[cd] %@",searchText)).sorted(byKeyPath: "_title", ascending: true)

or

you can create array of titles before loading collection view Some where after you create self.card

Sign up to request clarification or add additional context in comments.

7 Comments

Thanks, Britto. But it seems it doesn't work, cause self.card is not a [String], it's kind of AWSDynamoDBObjectModel, AWSDynamoDBModeling. The error is Cannot convert value of type 'NSPredicate' to expected argument type '(RecommendArticles) throws -> Bool'. I just edited the question.
It does not need to be a string.. you can filter a array of any models with its properties.
Also you can't create array of title string in cellForItemAtIndexPath. Answer is updated please check
Thanks for all the help, Britto. Now it works, with the second way you suggested: create array of titles before loading collection view Some where after you create self.card. I'll put the answer as an update in the question.
Oops, it seems I celebrate too early. When I create an array of titles after self.card, the titles array will increase every time viewWillAppear. Because the I put the self.card under viewWillAppear. So the search result is incorrect when another view will appear, after the initial viewWillAppear. I put the codes in the question. Any ideas you can share with me? Thanks.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.