It seems that the keyboard doesn't have to take up the whole screen, check UPDATE in the Question section of my post. Thanks.
Use UITextField to place a full-screen keyboard on the screen.
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.
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)
}
}
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.
This is what I currently get. The text box opens a keyboard that takes up the whole screen.
I was finally able to implement this without having to use TVML templates
. The final solution looks something like this:
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
.
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