Skip to content

Commit

Permalink
Merge pull request #2 from graycampbell/search
Browse files Browse the repository at this point in the history
Add search functionality
  • Loading branch information
graycampbell committed Oct 2, 2017
2 parents 6c5480e + 301b6d2 commit 230ecf7
Show file tree
Hide file tree
Showing 7 changed files with 187 additions and 29 deletions.
8 changes: 8 additions & 0 deletions GCCountryPicker.xcodeproj/project.pbxproj
Expand Up @@ -8,6 +8,8 @@

/* Begin PBXBuildFile section */
E2644B401F7D92DD0034EA06 /* GCCountryPicker.h in Headers */ = {isa = PBXBuildFile; fileRef = E2644B3E1F7D92DD0034EA06 /* GCCountryPicker.h */; settings = {ATTRIBUTES = (Public, ); }; };
E27C15561F81955600DA9091 /* GCSearchResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27C15551F81955600DA9091 /* GCSearchResultsController.swift */; };
E27C15581F81963600DA9091 /* GCSearchResultsDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E27C15571F81963600DA9091 /* GCSearchResultsDelegate.swift */; };
E2F9BFAE1F7D959A00131AF8 /* GCCountryPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F9BFAD1F7D959A00131AF8 /* GCCountryPickerViewController.swift */; };
E2F9BFB11F7D97D000131AF8 /* GCCountryPickerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F9BFB01F7D97D000131AF8 /* GCCountryPickerDelegate.swift */; };
E2F9BFB51F7DA08900131AF8 /* GCCountry.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2F9BFB41F7DA08900131AF8 /* GCCountry.swift */; };
Expand All @@ -18,6 +20,8 @@
E2644B3B1F7D92DD0034EA06 /* GCCountryPicker.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GCCountryPicker.framework; sourceTree = BUILT_PRODUCTS_DIR; };
E2644B3E1F7D92DD0034EA06 /* GCCountryPicker.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GCCountryPicker.h; sourceTree = "<group>"; };
E2644B3F1F7D92DD0034EA06 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
E27C15551F81955600DA9091 /* GCSearchResultsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCSearchResultsController.swift; sourceTree = "<group>"; };
E27C15571F81963600DA9091 /* GCSearchResultsDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCSearchResultsDelegate.swift; sourceTree = "<group>"; };
E2F9BFAD1F7D959A00131AF8 /* GCCountryPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCCountryPickerViewController.swift; sourceTree = "<group>"; };
E2F9BFB01F7D97D000131AF8 /* GCCountryPickerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCCountryPickerDelegate.swift; sourceTree = "<group>"; };
E2F9BFB41F7DA08900131AF8 /* GCCountry.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GCCountry.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -58,6 +62,8 @@
E2F9BFB41F7DA08900131AF8 /* GCCountry.swift */,
E2F9BFB01F7D97D000131AF8 /* GCCountryPickerDelegate.swift */,
E2F9BFAD1F7D959A00131AF8 /* GCCountryPickerViewController.swift */,
E27C15571F81963600DA9091 /* GCSearchResultsDelegate.swift */,
E27C15551F81955600DA9091 /* GCSearchResultsController.swift */,
E2F9BFB61F7DA32500131AF8 /* CountryCodes.plist */,
E2644B3F1F7D92DD0034EA06 /* Info.plist */,
);
Expand Down Expand Up @@ -145,9 +151,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
E27C15561F81955600DA9091 /* GCSearchResultsController.swift in Sources */,
E2F9BFAE1F7D959A00131AF8 /* GCCountryPickerViewController.swift in Sources */,
E2F9BFB11F7D97D000131AF8 /* GCCountryPickerDelegate.swift in Sources */,
E2F9BFB51F7DA08900131AF8 /* GCCountry.swift in Sources */,
E27C15581F81963600DA9091 /* GCSearchResultsDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
2 changes: 0 additions & 2 deletions GCCountryPicker/GCCountryPicker.h
Expand Up @@ -14,5 +14,3 @@ FOUNDATION_EXPORT double GCCountryPickerVersionNumber;
FOUNDATION_EXPORT const unsigned char GCCountryPickerVersionString[];

// In this header, you should import all the public headers of your framework using statements like #import <GCCountryPicker/PublicHeader.h>


4 changes: 2 additions & 2 deletions GCCountryPicker/GCCountryPickerDelegate.swift
Expand Up @@ -7,7 +7,7 @@

import UIKit

/// The delegate of a GCCountryPickerViewController object must adopt the GCCountryPickerDelegate protocol. The protocol's optional methods allow the delegate to handle country selection and customize the picker's appearance.
/// The delegate of a GCCountryPickerViewController object must adopt the GCCountryPickerDelegate protocol.

public protocol GCCountryPickerDelegate {

Expand All @@ -26,7 +26,7 @@ public protocol GCCountryPickerDelegate {
/// ---
///
/// Your delegate’s implementation of this method should pass the country on to any custom code that needs it, and then it should dismiss the picker view.

///
/// - Parameter countryPicker: The controller object managing the country picker interface.
/// - Parameter country: The country selected by the user.

Expand Down
78 changes: 56 additions & 22 deletions GCCountryPicker/GCCountryPickerViewController.swift
Expand Up @@ -9,37 +9,37 @@ import UIKit

// MARK: Properties & Initializers

/// The GCCountryPickerViewController class defines a view controller containing a country picker interface.

public final class GCCountryPickerViewController: UITableViewController {

// MARK: Properties

/// The object that acts as the delegate of the country picker view controller.

public var delegate: GCCountryPickerDelegate?

fileprivate var countries = [GCCountry]()
fileprivate var searchController: UISearchController!
fileprivate var searchResultsController = GCSearchResultsController()

// MARK: Initializers

public required init?(coder aDecoder: NSCoder) {

super.init(coder: aDecoder)
}

public init(navigationTitle: String) {

super.init(style: .plain)

self.navigationItem.title = navigationTitle
}
/// Initializes and returns a newly allocated country picker view controller object.
///
/// - Returns: An initialized country picker view controller object.

public convenience init() {

self.init(navigationTitle: "Country")
self.init(style: .plain)

self.navigationItem.title = "Country"
}
}

// MARK: - View

public extension GCCountryPickerViewController {
extension GCCountryPickerViewController {

public override func viewDidLoad() {

Expand Down Expand Up @@ -81,16 +81,55 @@ extension GCCountryPickerViewController {
fileprivate func configureNavigationBar() {

self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .cancel, target: self, action: #selector(self.cancel(barButtonItem:)))

self.searchController = UISearchController(searchResultsController: self.searchResultsController)

self.searchController.searchResultsUpdater = self
self.searchController.searchBar.delegate = self.searchResultsController
self.searchResultsController.delegate = self

self.navigationItem.searchController = self.searchController
self.navigationItem.hidesSearchBarWhenScrolling = false

self.definesPresentationContext = true
}

// MARK: Targets

@objc func cancel(barButtonItem: UIBarButtonItem) {

self.delegate?.countryPickerDidCancel(self)
}
}

// MARK: - UISearchResultsUpdating

extension GCCountryPickerViewController: UISearchResultsUpdating {

public func updateSearchResults(for searchController: UISearchController) {

var searchResults = [GCCountry]()

if let searchText = searchController.searchBar.text?.localizedLowercase.replacingOccurrences(of: "(^\\s+)|(\\s+$)", with: "", options: .regularExpression, range: nil) {

if !searchText.isEmpty {

searchResults = self.countries.filter { $0.localizedDisplayName.localizedLowercase.range(of: "\\b\(searchText)", options: .regularExpression, range: nil, locale: .current) != nil }
}
}

self.searchResultsController.searchResults = searchResults
}
}

// MARK: - GCSearchResultsDelegate

extension GCCountryPickerViewController: GCSearchResultsDelegate {

func searchResultsController(_ searchResultsController: GCSearchResultsController, didSelectSearchResult searchResult: GCCountry) {

self.delegate?.countryPicker(self, didSelectCountry: searchResult)
}
}

// MARK: - Table View

extension GCCountryPickerViewController {
Expand All @@ -103,12 +142,7 @@ extension GCCountryPickerViewController {

// MARK: - UITableViewDelegate

public extension GCCountryPickerViewController {

public override func numberOfSections(in tableView: UITableView) -> Int {

return 1
}
extension GCCountryPickerViewController {

public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

Expand All @@ -123,7 +157,7 @@ public extension GCCountryPickerViewController {

// MARK: - UITableViewDataSource

public extension GCCountryPickerViewController {
extension GCCountryPickerViewController {

public override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

Expand Down
97 changes: 97 additions & 0 deletions GCCountryPicker/GCSearchResultsController.swift
@@ -0,0 +1,97 @@
//
// GCSearchResultsController.swift
// GCCountryPicker
//
// Created by Gray Campbell on 10/1/17.
//

import UIKit

// MARK: Properties & Initializers

/// The GCSearchResultsController class defines a view controller containing a search results interface.

class GCSearchResultsController: UITableViewController {

// MARK: Properties

/// The object that acts as the delegate of the search results view controller.

var delegate: GCSearchResultsDelegate?

var searchResults = [GCCountry]() {

didSet {

self.tableView.reloadData()
}
}
}

// MARK: - View

extension GCSearchResultsController {

override func viewDidLoad() {

super.viewDidLoad()

self.configureTableView()
}
}

// MARK: - UISearchBarDelegate

extension GCSearchResultsController: UISearchBarDelegate {

func searchBarTextDidBeginEditing(_ searchBar: UISearchBar) {

if !self.searchResults.isEmpty {

let rect = CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width, height: 0.5)

self.tableView.scrollRectToVisible(rect, animated: true)
}
}
}

// MARK: - Table View

extension GCSearchResultsController {

fileprivate func configureTableView() {

self.tableView.keyboardDismissMode = .onDrag

self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "TableViewCell")
}
}

// MARK: - UITableViewDelegate

extension GCSearchResultsController {

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {

return self.searchResults.count
}

override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

self.delegate?.searchResultsController(self, didSelectSearchResult: self.searchResults[indexPath.row])
}
}

// MARK: - UITableViewDataSource

extension GCSearchResultsController {

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {

let cell = tableView.dequeueReusableCell(withIdentifier: "TableViewCell", for: indexPath)

cell.textLabel?.text = self.searchResults[indexPath.row].localizedDisplayName

return cell
}
}
20 changes: 20 additions & 0 deletions GCCountryPicker/GCSearchResultsDelegate.swift
@@ -0,0 +1,20 @@
//
// GCSearchResultsDelegate.swift
// GCCountryPicker
//
// Created by Gray Campbell on 10/1/17.
//

import UIKit

/// The delegate of a GCSearchResultsController object must adopt the GCSearchResultsDelegate protocol.

protocol GCSearchResultsDelegate {

/// Tells the delegate that the user picked a search result.
///
/// - Parameter searchResultsController: The controller object managing the search results interface.
/// - Parameter searchResult: The search result selected by the user.

func searchResultsController(_ searchResultsController: GCSearchResultsController, didSelectSearchResult searchResult: GCCountry)
}
7 changes: 4 additions & 3 deletions GCCountryPickerDemo/ViewController.swift
Expand Up @@ -12,7 +12,9 @@ import GCCountryPicker

class ViewController: UIViewController {

// MARK: Properties

fileprivate var country: GCCountry!
}

// MARK: - View
Expand All @@ -34,11 +36,9 @@ extension ViewController {
fileprivate func configureNavigationBar() {

self.navigationItem.title = Locale.current.localizedString(forRegionCode: "US")
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Change Country", style: .plain, target: self, action: #selector(showCountryPicker(barButtonItem:)))
self.navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit, target: self, action: #selector(self.showCountryPicker(barButtonItem:)))
}

// MARK: Targets

@objc func showCountryPicker(barButtonItem: UIBarButtonItem) {

let countryPickerViewController = GCCountryPickerViewController()
Expand All @@ -62,6 +62,7 @@ extension ViewController: GCCountryPickerDelegate {

func countryPicker(_ countryPicker: GCCountryPickerViewController, didSelectCountry country: GCCountry) {

self.country = country
self.navigationItem.title = country.localizedDisplayName

self.dismiss(animated: true, completion: nil)
Expand Down

0 comments on commit 230ecf7

Please sign in to comment.