LoginSignup
49
50

More than 5 years have passed since last update.

【Swift】iOSでSuggestキーワードを表示してみた

Last updated at Posted at 2015-05-18

こんな感じです。

SwiftSampleSuggestView.gif

とるの下手くそでごめんさない。

簡単な動作を説明すると

  1. UISearchViewをNavigationBarに入れる
  2. delegateで入力された値を取得する
  3. YoutubeのSuggestAPIにアクセスする
  4. 結果をStringの配列にしてUITableViewに表示する

という感じです。

ファイルは以下におきました。

ryokosuge/SampleSwiftSuggestView

SuggestのAPIについては以下を参考にしました。

http://logic.moo.jp/data/archives/792.html

にすべて書いてあります。

ここの Youtube の部分を参考にさせていただきました。

特に難しいこともしていませんので、Suggestを表示するまでの流れを紹介します。

ソースコード

ちょっと長いですが、こんな感じです。

SuggestViewController.swift
import UIKit
import BrightFutures

class SuggestViewController: UIViewController {

    dynamic var suggestItems: [String] = []

    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var constTableViewHeight: NSLayoutConstraint!

    var searchBar: UISearchBar!

    override func viewDidLoad() {
        super.viewDidLoad()
        println(__FUNCTION__)
        setupSearchBar()
        setupTableView()
        navigationController?.setNavigationBarHidden(false, animated: true)
    }

    override func viewDidAppear(animated: Bool) {
        super.viewDidAppear(animated)
    }

    deinit {
        self.removeObserver(self, forKeyPath: "suggestItems")
        tableView.removeObserver(self, forKeyPath: "contentSize")
    }
}

// MARK : - set up

extension SuggestViewController {

    private func setupSearchBar() {
        if let navigationBarFrame = navigationController?.navigationBar.bounds {
            let searchBar: UISearchBar = UISearchBar(frame: navigationBarFrame)
            searchBar.delegate = self
            searchBar.showsCancelButton = true
            searchBar.keyboardAppearance = UIKeyboardAppearance.Dark
            searchBar.keyboardType = UIKeyboardType.Default
            navigationItem.titleView = searchBar
            navigationItem.titleView?.frame = searchBar.frame
            self.searchBar = searchBar
            searchBar.becomeFirstResponder()
        }
    }

    private func setupTableView() {
        tableView.delegate = self
        tableView.dataSource = self
        tableView.tableFooterView = UIView()
        tableView.hidden = true

        self.addObserver(self, forKeyPath: "suggestItems", options: NSKeyValueObservingOptions.New, context: nil)
        tableView.addObserver(self, forKeyPath: "contentSize", options: (NSKeyValueObservingOptions.New), context: nil)
    }
}

// MARK : - tableView height observer

extension SuggestViewController {

    override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {
        println(__FUNCTION__)
        if keyPath == "contentSize" {
            didChangeTableViewContentHeight()
        } else if keyPath == "suggestItems" {
            didChangeSuggestItems()
        }
    }

    private func didChangeTableViewContentHeight() {
        println(__FUNCTION__)
        constTableViewHeight.constant = tableView.contentSize.height
        tableView.setNeedsLayout()
        tableView.setNeedsUpdateConstraints()
        UIView.animateWithDuration(0.6, animations: {[weak self] () -> Void in
            self?.tableView.layoutIfNeeded()
        })
    }

    private func didChangeSuggestItems() {
        dispatch_async(dispatch_get_main_queue(), {[weak self] () -> Void in
            self?.tableView.hidden = false
            self?.tableView.reloadData()
        })
    }
}

// MARK : - tableview delegate

extension SuggestViewController: UITableViewDelegate {

    func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
        tableView.deselectRowAtIndexPath(indexPath, animated: true)
    }
}

// MARK : - tableview dataSource

extension SuggestViewController: UITableViewDataSource {

    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }

    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let TableViewBasicCellIdentifier = "BasicCell"
        let cell = tableView.dequeueReusableCellWithIdentifier(TableViewBasicCellIdentifier, forIndexPath: indexPath) as! UITableViewCell
        cell.textLabel?.text = suggestItems[indexPath.row]
        return cell
    }

    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return suggestItems.count
    }
}

// MARK : - search bar delegate

extension SuggestViewController: UISearchBarDelegate {

    func searchBarCancelButtonClicked(searchBar: UISearchBar) {
        println(__FUNCTION__)
        searchBar.resignFirstResponder()
        navigationController?.setNavigationBarHidden(true, animated: true)
        dismissViewControllerAnimated(true, completion: nil)
    }

    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
        println(__FUNCTION__)
        println("searchText = \(searchText)")
        self.searchSuggestKeyword(searchText)
    }

}

// MARK : - search suggest keyword

extension SuggestViewController {

    private func searchSuggestKeyword(keyword: String) {
        if count(keyword) < 1 {
            return
        }

        futureSuggestRequest(keyword).onSuccess { suggestItems in
            self.suggestItems = suggestItems
        }.onFailure { error in
            println("error = \(error)")
        }

    }

    private func futureSuggestRequest(searchKeywork: String) -> Future<[String]> {
        println(__FUNCTION__)
        let promiss = Promise<[String]>()

        Queue.global.async {
            if let URL = self.createSuggestURL(searchKeywoard: searchKeywork) {
                let request = self.requestURL(URL)
                self.sendRequest(request).flatMap { data in
                    self.futureSuggestItems(data: data)
                }.onSuccess { suggestItems in
                    promiss.success(suggestItems)
                }.onFailure { error in
                    promiss.failure(error)
                }
            }
        }

        return promiss.future
    }

    private func sendRequest(request: NSURLRequest) -> Future<NSData> {
        println(__FUNCTION__)
        let promiss = Promise<NSData>()
        let session = NSURLSession.sharedSession()
        let task = session.dataTaskWithRequest(request, completionHandler: { (responseData: NSData!, urlResponse: NSURLResponse?, connectError: NSError?) -> Void in
            if let error = connectError {
                promiss.failure(error)
            } else {
                promiss.success(responseData)
            }
        })
        task.resume()
        return promiss.future
    }

    private func futureSuggestItems(#data: NSData) -> Future<[String]> {
        println(__FUNCTION__)
        let promiss = Promise<[String]>()
        if let dataString = NSString(data: data, encoding: NSShiftJISStringEncoding), d = dataString.dataUsingEncoding(NSUTF8StringEncoding) {
            var error: NSError? = nil
            if let jsonArray = NSJSONSerialization.JSONObjectWithData(d, options: NSJSONReadingOptions.AllowFragments, error: &error) as? NSArray {
                let suggestItems = convertSuggestItems(jsonArray)
                promiss.success(suggestItems)
            } else {
                if let e = error {
                    promiss.failure(e)
                }
            }
        } else {
            let error = NSError(domain: "SampleSuggest", code: 9999, userInfo: nil)
            promiss.failure(error)
        }
        return promiss.future
    }

    private func convertSuggestItems(jsonArray: NSArray) -> [String] {
        println(__FUNCTION__)
        var newSuggestWords: [String] = []
        if let strs = jsonArray.lastObject as? [String] {
            for str in strs {
                newSuggestWords.append(str)
            }
        }

        return newSuggestWords
    }

    private func requestURL(URL: NSURL) -> NSURLRequest {
        println(__FUNCTION__)
        var request = NSMutableURLRequest(URL: URL)
        request.setValue("application/json", forHTTPHeaderField: "Accept")
        request.setValue("applicatoin/json; charset=UTF-8", forHTTPHeaderField: "Content-Type")
        request.HTTPMethod = "GET"
        return request
    }

    private func createSuggestURL(#searchKeywoard: String) -> NSURL? {
        println(__FUNCTION__)
        let urlString = "http://clients1.google.com/complete/search?hl=ja&ds=yt&client=firefox&q=\(searchKeywoard.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!)"
        return NSURL(string: urlString)
    }

}

以下ソースコードの要約になります。

BrightFutures

Thomvis/BrightFutures

とっても使ってみたかったライブラリについに手を出しました。

以下の記事が参考になりました。

[Swift] 非同期処理フレームワークBrightFutures ~導入編~ | Developers.IO

使い方などはGitHubを見れば大体わかるかと思います!

これに関しては別記事で書いた方がわかりやすいかと。

View的になにをしているか

見た目の話をします。

以下のソースで Suggestの結果を表すUITableViewcontentSizeを監視しています。

tableView.addObserver(self, forKeyPath: "contentSize", options: (NSKeyValueObservingOptions.New), context: nil)

これでtableViewcontentSizeが変化したら

override func observeValueForKeyPath(keyPath: String, ofObject object: AnyObject, change: [NSObject : AnyObject], context: UnsafeMutablePointer<Void>) {}

がコールされるので、それにcontentSizeheightに合わせてtableViewHeightConstraintに当たるconstTableViewHeightを変化させています。

つまりsuggestItems:[String]が空の場合、表示するものがなにもないのでconstTableViewHeight.constant0になり、表示されなくなります。

最初はStoryboardhidden = trueにしています。(めんどくさかっただけです)

終わり

様々な処理を一つのクラスにまとめましたが、ちゃんと分けるように書かないとダメですね。

以上です。

レポジトリ

49
50
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
49
50