Search code examples
swifttvosuikeyboard

Does the keyboard for a UITextField have to take up the whole screen?


It seems that the keyboard doesn't have to take up the whole screen, check UPDATE in the Question section of my post. Thanks.

Description

Use UITextField to place a full-screen keyboard on the screen.

reference

I've set up a UISplitViewController and I would like the RootViewController (aka MasterViewController) to have the UITextField with the Keyboard showing. Then I would like results of the search on the right (in the "ResultViewController" (UIViewController).

The idea is when the user types, results are proposed.

What I've tried:

I first added a UITextField to my RootViewController via the storyboard but that took up the whole screen when I activated the keyboard via textField.becomeFirstResponder().

I figured if I use a UIAlertController I'd get by this issue, but the keyboard still takes up the whole screen.

class RootViewController: UIViewController {

 override func viewDidLoad() {

    let alertController = UIAlertController(title: "Search", message: "Search for something!.", preferredStyle: UIAlertControllerStyle.Alert)

    alertController.addTextFieldWithConfigurationHandler({(textField: UITextField!) in
        textField.placeholder = "Search"
    })
    self.addChildViewController(alertController)
    alertController.view.frame = self.view.frame
    self.view.addSubview(alertController.view)
 }
}

Question:

When using a UISplitViewController how can I get the keyboard to only stay in it's RootViewController and not take up the whole screen?

UPDATE: It appears this has been implemented in the Netflix app on the new apple tv. The keyboard is on the top and takes up the whole width. The bottom is divided into two sections. On the left, proposed word results and on the right a collection view of video covers of possible videos. Everything is updated as the user types.

If this is considered bad design for Apple TV, then feel free to point out why.

Screen shot:

This is what I currently get. The text box opens a keyboard that takes up the whole screen.

enter image description here


Solution

  • Introduction:

    I was finally able to implement this without having to use TVML templates. The final solution looks something like this:

    text keyboard and results on one view

    The general idea is to create a UICollectionViewController with a UICollectionViewCell. Then to programmatically add a keyboard and add it to your TabViewController via your AppDelegate.


    How to implement this search view with results:

    Step 1: Storyboard and Controller creation

    Open your storyboard and create a UICollectionViewController (with a custom class "SearchResultViewController") that is not attached to your TabViewController.

    Within it create a your UICollectionViewCell with whatever labels and images you want. UICollectionViewCell should have a custom class called "VideoSearchCell".

    Your SearchViewController should have nothing else inside.

    Step 2: Adding SearchViewController to TabViewController and implementing keyboard via AppDelegate programmatically

    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
    
        override init() {
    
        }
        func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
            // Override point for customization after application launch.
            if let tabController = window?.rootViewController as? UITabBarController {
                tabController.viewControllers?.append(configueSearchController())
            }
    
            return true
        }
    
        //... standard code in-between
    
        func configueSearchController() -> UIViewController {
    
            let storyboard = UIStoryboard(name: "Main", bundle: nil)
            guard let searchResultController = storyboard.instantiateViewControllerWithIdentifier(SearchResultViewController.storyboardIdentifier) as? SearchResultViewController else {
                fatalError("Unable to instatiate a SearchResultViewController from the storyboard.")
            }
    
            /*
             Create a UISearchController, passing the `searchResultsController` to
             use to display search results.
             */
            let searchController = UISearchController(searchResultsController: searchResultsController)
            searchController.searchResultsUpdater = searchResultsController
            searchController.searchBar.placeholder = NSLocalizedString("Enter keyword (e.g. Gastric Bypass)", comment: "")
    
            // Contain the `UISearchController` in a `UISearchContainerViewController`.
            let searchContainer = UISearchContainerViewController(searchController: searchController)
            searchContainer.title = NSLocalizedString("Search", comment: "")
    
            // Finally contain the `UISearchContainerViewController` in a `UINavigationController`.
            let searchNavigationController = UINavigationController(rootViewController: searchContainer)
            return searchNavigationController
    
        }
    
    }
    

    Once you've added the basic skeleton of your SearchResultViewController you should be able to see the keyboard on the top of the Search view when you run your project.

    Step 3: Handling text input and updating results

    You'll notice that in my filterString I use a class called ScoreVideo and a StringSearchService. These are just classes I use to filter my Video list (aka: self.vms.videos).

    So in the end, just take the filterString, create a new filtered list and reload your collection view.

    import UIKit
    import Foundation
    
    class SearchResultViewController: UICollectionViewController, UISearchResultsUpdating {
    
    
        //private let cellComposer = DataItemCellComposer()
        private var vms: VideoManagerService!
    
        private var filteredVideos = [ScoreVideo]()
        static let storyboardIdentifier = "SearchResultViewController"
    
        var filterString = "" {
            didSet {
                // Return if the filter string hasn't changed.
                guard filterString != oldValue else { return }
                    // Apply the filter or show all items if the filter string is empty.
                    if self.filterString.isEmpty {
                        self.filteredVideos = StringSearchService.start(self.filterString, videos: self.vms.videos)
                    }
                    else {
                        self.filteredVideos = StringSearchService.start(self.filterString, videos: self.vms.videos)
                    }
               self.collectionView?.reloadData()
            }
        }
    
        override func viewDidLoad() {
            let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
            self.vms = appDelegate.getVideoManagerService()
    
        }
    
        // MARK: UICollectionViewDataSource
    
        override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
            return 1
        }
    
        override func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
            print("count..\(filteredVideos.count)")
            return filteredVideos.count
        }
    
        override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
            // Dequeue a cell from the collection view.
            return collectionView.dequeueReusableCellWithReuseIdentifier(VideoSearchCell.reuseIdentifier, forIndexPath: indexPath)
        }
    
        // MARK: UICollectionViewDelegate
    
        override func collectionView(collectionView: UICollectionView, willDisplayCell cell: UICollectionViewCell, forItemAtIndexPath indexPath: NSIndexPath) {
            guard let cell = cell as? VideoSearchCell else { fatalError("Expected to display a `VideoSearchCell`.") }
            let item = filteredVideos[indexPath.row]
            cell.configureCell(item.video)
    
        }
    
        override func collectionView(collectionView: UICollectionView, didSelectItemAtIndexPath indexPath: NSIndexPath) {
            dismissViewControllerAnimated(true, completion: nil)
        }
    
        // MARK: UISearchResultsUpdating
    
        func updateSearchResultsForSearchController(searchController: UISearchController) {
            print("updating... \(searchController.searchBar.text)")
            filterString = searchController.searchBar.text!.lowercaseString ?? ""
        }
    }
    

    If something is unclear, feel free to ask some questions. I most likely forgot something. Thanks.

    Answer inspired from apple sample code