So far, you learned how to implement these features of the UICollectionView class:
Today you’ll learn how to implement these features in the SwiftCollectionView application:
- Search the collection view by typing text in a Search Bar’s text field.
- Dismiss the keyboard by tapping the Search Bar’s Cancel button.
- Dismiss the keyboard by double-tapping the collection view’s background.
Take a look at this QuickTime video, it show results of implementing the search bar and keyboard dismissal feature in the SwiftCollectionView application.
Create a New Branch
Ok, start things off by launching the SwiftCollectionView project in Xcode. Use the Source Control menu to create a new branch. Call it search-collection-view.
Add New Scene on The Storyboard
Now, in order to implement the search feature in the project, you’ll have to add a new scene on the storyboard canvas and configure it so it look like the one shown in Figure 6-2 below. Start by clicking the Main.storyboard file to load it in Interface Builder. Now do the following:
- Delete the Collection View Controller scene and its Navigation Controller.
- Drag a View Controller from the Object Library and drop it on the storyboard canvas.
- Embed the View Controller scene in a Navigation Controller (Editor | Embed In | Navigation Controller).
- Click the Collection View scene’s Navigation Controller scene.
- In the Attributes inspector, tick the Is Initial View Controller checkbox. This make the Collection View scene the initial scene.
- Drag a Search Bar from the Object Library and drop it below the View Controller scene’ Navigation Item. Make sure it is the same width as the scene-see Figure 6-2 below.
- Click the Search Bar. Locate the Placeholder field in the Attributes Inspector and enter this text: Enter the photo name here
- Locate the Shows Cancel Button checkbox in the Attributes Inspector and tick it. This place a Cancel button on the Search Bar-see Figure 6-2 below.
- Drag a Collection View from the Object Library and drop it below the Search Bar. Make sure it cover remaining surface of the scene-see Figure 6-2 below.
- Click the Collection View Cell, locate the Identifier field in the Attributes Inspector, enter this text: Cell. Next, locate the Keyboard menu. It’s located in the Scroll View section. Select the Dismiss on drag option. This dismiss the keyboard and reset the collection view back its default mode.
- Control-click the Collection View Cell and drag resulting blue line to the Photo Detail scene’s Navigation Bar Scene. Release your mouse when the Navigation Controller scene is highlighted. Select present modally from the pop up menu.
- Click the segue you created for the Collection View Cell. Locate the Identifier field in the Attributes Inspector and enter this text: photoDetail
- Click the Collection View. In the Size Inspector, add sizes shown in Figure 6-1 below.
- Click the View Controller scene’s Navigation Item. Locate the Title field in the Attributes Inspector and enter this text: Photos
- Click the View Controller scene object, locate the Class menu in the Identity Inspector, and select MasterViewController option.
- Select the View Controller object and use the Auto Layout bar’s Issues button to add missing constraints to the default Size Class; which is wAny hAny.
That’s it you are done designing the new scene. Rearrange the scenes so they look like Figure 6-3.
Figure 6-1 | Figure 6-2 |
Figure 6-3 |
The Search Bar and Collection View Connections
In Interface Builder, you have to do the following for the Search Bar and the Collection View you added on the Photos scene:
- Connect the Search Bar’s delegate outlet to the Photos scene object via the Connections Inspector.
- Create and connect an IBOutlet variable called searchBar via the Assistant editor.
- Connect the Collection View’s dataSource and delegate outlets via the Connections Inspector.
- Create and connect an IBOutlet variable called collectionView via the Assistant Editor.
Once you’ve perform above tasks, the Connections Inspector for the Photos scene’s Search Bar and the Collection View should look like these images:
Figure 6-4: Search Bar Connections | Figure 6-5: Collection View Connections |
The Master View Controller Class Code
That’s it; you are done with the Main.storyboard, so switch to the MasterViewController.swift file because you will be adding code in it to implement the collection view search feature and to dismiss the on-screen keyboard.
Start by scrolling to the end of the MasterViewController.swift file and add code shown below, just above the class file’s closing } bracket.
// MARK: UISearchBarDelegate functions // This function is fired when the user tap the Search Bar's Cancel button func searchBarCancelButtonClicked(searchBar: UISearchBar!) { searchBar.text = nil // Clear out the Search Bar's text field searchBar.showsCancelButton = false // Hide the Search Bar's Cancel button searchBar.resignFirstResponder() // Dismiss the keyboard isSearchOn = false // Turn off search function self.collectionView.reloadData() // Refresh the collection view } // this function is fired when the user start entering text in the Search Bar's text field func searchBar(searchBar: UISearchBar!, textDidChange searchText: String!) { searchBar.showsCancelButton = true // Show the Search Bar's Cancel button if !searchText.isEmpty { isSearchOn = true // Turn on searching function self.filterContentForSearchText() // Search the collection view's dataSource self.collectionView.reloadData() } }
Here are the helper functions for the UISearchBarDelegate functions. Add them in the MasterViewController.swift file.
// MARK: Helper functions for the UISearchBarDelegate functions // This function search the collection view's dataSource object (imageFileNames)for an item that match // text entered in the Search Bar's text field. If any items found, add them in the searchResults array func filterContentForSearchText() { // Remove all elements from the searchResults array searchResults.removeAll(keepCapacity: false) // Loop throught the collection view's dataSource object for imageFileName in imageFileNames { let stringToLookFor = imageFileName as NSString let sourceString = searchBar.text as NSString if stringToLookFor.localizedCaseInsensitiveContainsString(sourceString) { // Match found, so add it to the searchResults array variable searchResults.append(imageFileName) } } } // Target function for the tap gesture recognizer object created in the viewDidLoad function func collectionViewBackgroundTapped() { // Dismiss the keyboard that's shown on the device's screen searchBar.resignFirstResponder() } /*********************** This function allow the default tap gesture added to the collection view cell, an the collection view's background to to work simultaneously **************************/ func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRequireFailureOfGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool { // Perform default tap gesture return true } }
Now, scroll up to the top of the MasterViewController.swift and change its code to this:
import UIKit let reuseIdentifier = "Cell" class MasterViewController: UIViewController, UISearchBarDelegate, UIGestureRecognizerDelegate { @IBOutlet weak var editButton: UIBarButtonItem! @IBOutlet weak var searchBar: UISearchBar! @IBOutlet weak var collectionView: UICollectionView! var icon: PhotoCell! var imageFileNames = [String]() var selectedPhotoName = String() var isSearchOn = false var searchResults = [String]()
Modify the viewDidLoad function’s code to this:
override func viewDidLoad() { super.viewDidLoad() // Set the collection view's backgroundcolor to display an image self.collectionView!.backgroundColor = UIColor(patternImage: UIImage(named: "purty_wood")!) // Register the class with the collection view self.collectionView!.registerClass(PhotoCell.self, forCellWithReuseIdentifier: reuseIdentifier) // Add objects in the collection view dataSource, imageFileNames Array loadImages() //1. Create a tap gesture recognizer object let tapRecognizer = UITapGestureRecognizer() tapRecognizer.numberOfTapsRequired = 2 //2. Set the target function for the tap gesture recognizer object tapRecognizer.addTarget(self, action: "collectionViewBackgroundTapped") //3. Add the tap gesture recognizer to the collection view self.collectionView.addGestureRecognizer(tapRecognizer) }
Scroll down to the collection view data source functions and change their code to those shown below:
func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int { // Return the number of sections return 1 } func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { // Return the number of items in the section // Return the number of items in a section (number of photos in an album) if isSearchOn == true && !searchResults.isEmpty { return searchResults.count } else { return imageFileNames.count } } func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { // Initialize the reusable Collection View Cell with our custom class icon = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath) as PhotoCell var photoName = String() // Initialize the photoName variable with an item in the searchResults array or an item in the imageFileNames array if isSearchOn == true && !searchResults.isEmpty { photoName = searchResults[indexPath.item] } else { photoName = imageFileNames[indexPath.item] } // Configure the collection view cell icon.imageView.image = UIImage(named: photoName) var stringArray: Array = photoName.componentsSeparatedByString(".") icon.caption.text = stringArray[0] // Return the cell return icon }
Also, change the collection view delegate function’s code to this:
// MARK: UICollectionViewDelegate func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) { // Put the selected collection view cell's photo file name in a variable if isSearchOn == true && !searchResults.isEmpty { selectedPhotoName = searchResults[indexPath.row] as String } else { selectedPhotoName = imageFileNames[indexPath.row] as String } // Pass control to the PhotoDetailViewController self.performSegueWithIdentifier("photoDetail", sender:self) }
The last thing I want you to do is, change the PhotoCell.swift file’s code so it look like this:
import UIKit class PhotoCell: UICollectionViewCell { // The collection view cell's objects var imageView: UIImageView! var caption: UILabel! required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) } override init(frame: CGRect) { super.init(frame: frame) // Create an ImageView and add it to the collection view imageView = UIImageView(frame: CGRect(x:30, y:12, width:55, height:55)) imageView.contentMode = UIViewContentMode.ScaleAspectFill contentView.addSubview(imageView) // Create a Label view and add it to the collection view let textFrame = CGRect(x:5, y:67, width:100, height:35) caption = UILabel(frame: textFrame) caption.font = UIFont.systemFontOfSize(14.0) caption.textAlignment = .Center caption.numberOfLines = 2 caption.lineBreakMode = NSLineBreakMode.ByWordWrapping caption.textColor = UIColor.whiteColor() caption.backgroundColor = UIColor.blackColor() contentView.addSubview(caption) } }
Go ahead and run the app in the iOS Simulator and test the Search Bar’s and code, and the keyboard dismissal code. The app should function just like the one shown in above QuickTime video.
That’s All Folks!
That’s all for phase 6 of the SwiftCollectionView app. Next week, you will learn how to implement these features in the app:
- Fetch image urls from a plist file, add them in the collection view’s dataSource variable.
- Load the photo urls’ images in the collection view cells
. Until then, happy coding! 🙂