无法呈现 UISearchController [英] Unable to present a UISearchController
问题描述
我有以下视图层次结构:
UINavigationController||\/LibraryTableViewController:UITableViewController||\/相册CollectionViewController:UICollectionViewController||\/SongsTableViewController: UITableViewController
我希望在 AlbumsCollectionViewController
中有一个搜索栏,而在 navigationItem.titleView
中显示的 SongsTableViewController
中有一个不同的搜索栏.>
我设法在 AlbumsCollectionViewController
中添加了一个有效的搜索栏,如下所示:
class AlbumsCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate {var searchController : UISearchController!覆盖 func viewDidLoad() {super.viewDidLoad()initSearchBar()initNavigationBar()}私有函数 initSearchBar() {self.searchController = UISearchController(searchResultsController: nil)self.searchController.searchResultsUpdater = selfself.searchController.delegate = selfself.searchController.searchBar.delegate = selfself.searchController.hidesNavigationBarDuringPresentation = falseself.searchController.dimsBackgroundDuringPresentation = falsesearchController.searchResultsController?.view.isHidden = falsesearchController.hidesNavigationBarDuringPresentation = falseself.extendedLayoutIncludesOpaqueBars = trueself.definesPresentationContext = truesearchController.searchBar.backgroundColor = UIColor.blackUIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.white], for: .normal)self.navigationItem.titleView = searchController.searchBarnavigationItem.titleView?.isHidden = true}私有函数 initNavigationBar() {searchButton.tintColor = UIColor.whitesettingsButton.tintColor = UIColor.whitebackButton.tintColor = UIColor.whiteself.navigationItem.title = "艺术家"self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]}@IBAction func SearchButtonTapped(_ sender: Any) {显示搜索栏()}私人功能 showSearchBar(){navigationItem.titleView?.isHidden = falsesearchController.isActive = 真}}
请注意,搜索栏隐藏在 ViewDidLoad()
上,并在按下按钮时显示,如 SearchButtonTapped
方法所示.
现在,我试图在 SongsTableViewController
中做同样的事情,但是,点击按钮时搜索栏没有显示(即调用 SearchButtonTapped
),我得到以下消息:
Warning: Attempt to present 在 <MyProject.AlbumsCollectionViewController: 0x7f81588023c0>其视图不在窗口层次结构中!
如果我评论了 searchController.isActive = true
行,那么搜索栏将显示,但是,即使我点击它也不会处于活动状态.
编辑
对不起,如果我没有说清楚.我在 SongsTableViewController
中有一个单独的 UISearchController
.我的意思是我在两个控制器中使用相同的逻辑
另外请注意,如果我从导航控制器推送 SongsTableViewController
(即视图层次结构只有 2 个控制器(UINavigationController => SongsTableViewController),搜索栏工作正常
这是SongsTableViewController
的大部分代码(省略不相关的东西)
导入 UIKit导入 os.log导入媒体播放器类 SongsTableViewController:UITableViewController、UISearchControllerDelegate、UISearchResultsUpdating、UISearchBarDelegate、PlayerDelegate、NowPlayingDelegate、SongCellDelegate、SongsOptionsDelegate {//标记:属性var playerManager:PlayerManager?= 零var 数据管理器:数据管理器?= 零var tabVC:TabBarController?var selectedSong:宋?惰性 var optionsTransitionDelegate = PresentationManager()懒惰 var playlistTransitionDelegate = PresentationManager()var searchController : UISearchController!@IBOutlet var backgroundView:UIView!@IBOutlet 弱 var searchButton: UIBarButtonItem!@IBOutlet 弱变量 settingsButton:UIBarButtonItem!var专辑ID:字符串?var 艺术家 ID:字符串?var 播放列表:播放列表?var song = [Song]()var songIndexMap = [String: Int]()varfilteredSongs = [Song]()覆盖 func viewDidLoad() {super.viewDidLoad()self.dataManager = DataManager.getInstance()self.playerManager = PlayerManager.getInstance()playlistTransitionDelegate.screenRatio = 2.0/3.0if(self.albumID != nil) {self.songs = SQLiteManager.getAlbumSongs(专辑ID:self.albumID!)} else if(self.artistID != nil) {self.songs = SQLiteManager.getArtistSongs(artistID: self.artistID!)} else if (self.playlist != nil) {self.songs = SQLiteManager.getPlaylistSongs(播放列表:self.playlist!)} 别的 {dataManager?.songsTableViewController = self}对于我在 0..<songs.count {SongIndexMap[songs[i].id] = i}initSearchBar()initNavigationBar()if(songs.count == 0 && (!fullListOfSongs() || fullListOfSongs() && dataManager?.getFullSongsCount() == 0)){tableView.backgroundView = backgroundView}tableView.tableFooterView = UIView()tabVC = tabBarController as?标签栏控制器tabVC?.nowPlayingViewController?.delegate = self}私有函数 shouldAutorotate() ->布尔{返回假}覆盖 func didReceiveMemoryWarning() {super.didReceiveMemoryWarning()}//MARK: - 表视图数据源覆盖 func numberOfSections(in tableView: UITableView) ->整数{返回 1}覆盖 func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) ->CGFloat {返回 Util.SONG_CELL_HEIGHT}覆盖 func tableView(_ tableView: UITableView, numberOfRowsInSection 部分: Int) ->整数{如果(isFiltering()){返回 self.filteredSongs.count} else if (fullListOfSongs()) {返回 dataManager!.getFullSongsCount()}返回 self.songs.count}覆盖 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) ->UITableViewCell {让 cellIdentifier = "SongTableViewCell"保护 let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as?SongCell 其他 {fatalError("出列的单元格不是 SongCell 的实例.")}var 歌:歌?如果(fullListOfSongs()){if(isFiltering()){歌曲 = self.filteredSongs[indexPath.row]} 别的 {Song = dataManager?.getSong(index: indexPath.row)}} 别的 {if(isFiltering()){歌曲 = self.filteredSongs[indexPath.row]} 别的 {歌曲 = self.songs[indexPath.row]}}cell.setAttributes(歌曲:歌曲!)cell.delegate = selfcell.preservesSuperviewLayoutMargins = false返回单元格}覆盖 func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){self.tableView.deselectRow(at: indexPath, 动画: false)}//MARK: - 搜索栏func updateSearchResults(for searchController: UISearchController) {如果(!searchController.isActive){隐藏搜索栏()tableView.reloadData()}if(isSearchBarEmpty()) {返回}filterSongs(过滤器:searchController.searchBar.text!)tableView.reloadData()}私人功能过滤器歌曲(过滤器:字符串){if(self.albumID != nil) {self.filteredSongs = SQLiteManager.getAlbumSongs(albumID: self.albumID!, filter: filter)} else if(self.artistID != nil) {self.filteredSongs = SQLiteManager.getArtistSongs(artistID: self.artistID!, filter: filter)} else if(self.playlist != nil) {self.filteredSongs = SQLiteManager.getPlaylistSongs(playlist: self.playlist!, filter: filter)}别的 {self.filteredSongs = SQLiteManager.getSongs(filter: filter)}}func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {如果搜索文本 == "" {tableView.reloadData()}}私有函数 initSearchBar() {self.searchController = UISearchController(searchResultsController: nil)self.searchController.searchResultsUpdater = selfself.searchController.delegate = selfself.searchController.searchBar.delegate = selfself.searchController.hidesNavigationBarDuringPresentation = falseself.searchController.dimsBackgroundDuringPresentation = falsesearchController.searchResultsController?.view.isHidden = falsesearchController.hidesNavigationBarDuringPresentation = falseself.extendedLayoutIncludesOpaqueBars = trueself.definesPresentationContext = truesearchController.searchBar.backgroundColor = UIColor.blackUIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.white], for: .normal)self.navigationItem.titleView = searchController.searchBarnavigationItem.titleView?.isHidden = true}私有函数 initNavigationBar() {searchButton.tintColor = UIColor.white如果(fullListOfSongs()){searchButton.isEnabled = falsedataManager?.buttons.append(searchButton)}settingsButton.tintColor = UIColor.whiteself.navigationItem.title = "歌曲"self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]}@IBAction func SearchButtonTapped(_ sender: Any) {显示搜索栏()}私人功能 showSearchBar(){self.navigationItem.titleView?.isHidden = falseself.searchController.isActive = trueDispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {self.searchController.searchBar.becomeFirstResponder()}navigationItem.rightBarButtonItems![0].isEnabled = falsenavigationItem.rightBarButtonItems![0].image = nilnavigationItem.rightBarButtonItems![1].isEnabled = falsenavigationItem.rightBarButtonItems![1].image = nil}私人功能隐藏搜索栏(){navigationItem.titleView?.isHidden = truenavigationItem.rightBarButtonItems![0].isEnabled = truenavigationItem.rightBarButtonItems![0].image = UIImage(named: "settings")navigationItem.rightBarButtonItems![1].isEnabled = truenavigationItem.rightBarButtonItems![1].image = UIImage(named: "search")}func isFiltering() ->布尔{如果(搜索控制器 == 零){返回假}返回 searchController.isActive &&!isSearchBarEmpty()}私有函数 isSearchBarEmpty() ->布尔{返回 searchController.searchBar.text?.isEmpty ??真的}私有函数 fullListOfSongs() ->布尔{返回 self.playlist == nil &&self.albumID == nil &&self.artistID == nil}}
这在我测试时有效.我相信问题出在 SongsTableViewController
:self.definesPresentationContext = false
.对于推送的视图控制器,这应该是 true
.(请参阅文档此处)
对于 SongsTableViewController
(推送视图控制器)添加以下内容:
override func viewWillAppear(_animated: Bool) {super.viewWillAppear(动画)self.definesPresentationContext = true}
并将其添加到 AlbumsCollectionViewController
(初始视图控制器):
override func viewWillDisappear(_animated: Bool) {super.viewWillDisappear(动画)self.definesPresentationContext = false}
I have the following view hierarchy:
UINavigationController
||
\/
LibraryTableViewController: UITableViewController
||
\/
AlbumsCollectionViewController: UICollectionViewController
||
\/
SongsTableViewController: UITableViewController
I want to have a search bar in AlbumsCollectionViewController
and a different one in SongsTableViewController
that is shown in the navigationItem.titleView
.
I have managed to add a working search bar in AlbumsCollectionViewController
as follows:
class AlbumsCollectionViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate {
var searchController : UISearchController!
override func viewDidLoad() {
super.viewDidLoad()
initSearchBar()
initNavigationBar()
}
private func initSearchBar() {
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsController?.view.isHidden = false
searchController.hidesNavigationBarDuringPresentation = false
self.extendedLayoutIncludesOpaqueBars = true
self.definesPresentationContext = true
searchController.searchBar.backgroundColor = UIColor.black
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.white], for: .normal)
self.navigationItem.titleView = searchController.searchBar
navigationItem.titleView?.isHidden = true
}
private func initNavigationBar() {
searchButton.tintColor = UIColor.white
settingsButton.tintColor = UIColor.white
backButton.tintColor = UIColor.white
self.navigationItem.title = "Artists"
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
}
@IBAction func SearchButtonTapped(_ sender: Any) {
showSearchBar()
}
private func showSearchBar(){
navigationItem.titleView?.isHidden = false
searchController.isActive = true
}
}
Note that the search bar is hidden on ViewDidLoad()
and is presented when a button is pressed as shown in SearchButtonTapped
method.
Now, I am trying to do the same in SongsTableViewController
however, the search bar is not showing when tapping the the button (i.e. calling SearchButtonTapped
) and I am getting the following message:
Warning: Attempt to present <UISearchController: 0x7f8158812b50> on <MyProject.AlbumsCollectionViewController: 0x7f81588023c0> whose view is not in the window hierarchy!
If I commented the line searchController.isActive = true
then the search bar will show, however, it wont be active even if I tapped on it.
Edit
Sorry if I haven't been clear. I have a separate UISearchController
in SongsTableViewController
. I meant I am using the same logic in both controllers
Also Note if I pushed SongsTableViewController
from the navigation controller (i.e the view hierarchy only has 2 controllers (UINavigationController => SongsTableViewController) the search bar works fine
This is most of the Code of SongsTableViewController
(omitted non relevant stuff)
import UIKit
import os.log
import MediaPlayer
class SongsTableViewController: UITableViewController, UISearchControllerDelegate, UISearchResultsUpdating, UISearchBarDelegate ,PlayerDelegate, NowPlayingDelegate, SongCellDelegate, SongsOptionsDelegate {
// MARK: properties
var playerManager: PlayerManager? = nil
var dataManager: DataManager? = nil
var tabVC: TabBarController?
var selectedSong: Song?
lazy var optionsTransitionDelegate = PresentationManager()
lazy var playlistTransitionDelegate = PresentationManager()
var searchController : UISearchController!
@IBOutlet var backgroundView: UIView!
@IBOutlet weak var searchButton: UIBarButtonItem!
@IBOutlet weak var settingsButton: UIBarButtonItem!
var albumID: String?
var artistID: String?
var playlist: Playlist?
var songs = [Song]()
var songIndexMap = [String: Int]()
var filteredSongs = [Song]()
override func viewDidLoad() {
super.viewDidLoad()
self.dataManager = DataManager.getInstance()
self.playerManager = PlayerManager.getInstance()
playlistTransitionDelegate.screenRatio = 2.0 / 3.0
if(self.albumID != nil) {
self.songs = SQLiteManager.getAlbumSongs(albumID: self.albumID!)
} else if(self.artistID != nil) {
self.songs = SQLiteManager.getArtistSongs(artistID: self.artistID!)
} else if (self.playlist != nil) {
self.songs = SQLiteManager.getPlaylistSongs(playlist: self.playlist!)
} else {
dataManager?.songsTableViewController = self
}
for i in 0..<songs.count {
songIndexMap[songs[i].id] = i
}
initSearchBar()
initNavigationBar()
if(songs.count == 0 && (!fullListOfSongs() || fullListOfSongs() && dataManager?.getFullSongsCount() == 0)){
tableView.backgroundView = backgroundView
}
tableView.tableFooterView = UIView()
tabVC = tabBarController as? TabBarController
tabVC?.nowPlayingViewController?.delegate = self
}
private func shouldAutorotate() -> Bool {
return false
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return Util.SONG_CELL_HEIGHT
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
if(isFiltering()) {
return self.filteredSongs.count
} else if (fullListOfSongs()) {
return dataManager!.getFullSongsCount()
}
return self.songs.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cellIdentifier = "SongTableViewCell"
guard let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as? SongCell else {
fatalError("The dequeued cell is not an instance of SongCell.")
}
var song: Song?
if(fullListOfSongs()) {
if(isFiltering()){
song = self.filteredSongs[indexPath.row]
} else {
song = dataManager?.getSong(index: indexPath.row)
}
} else {
if(isFiltering()){
song = self.filteredSongs[indexPath.row]
} else {
song = self.songs[indexPath.row]
}
}
cell.setAttributes(song: song!)
cell.delegate = self
cell.preservesSuperviewLayoutMargins = false
return cell
}
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath){
self.tableView.deselectRow(at: indexPath, animated: false)
}
// MARK: - Search Bar
func updateSearchResults(for searchController: UISearchController) {
if (!searchController.isActive) {
hideSearchBar()
tableView.reloadData()
}
if(isSearchBarEmpty()) {
return
}
filterSongs(filter: searchController.searchBar.text!)
tableView.reloadData()
}
private func filterSongs(filter: String) {
if(self.albumID != nil) {
self.filteredSongs = SQLiteManager.getAlbumSongs(albumID: self.albumID!, filter: filter)
} else if(self.artistID != nil) {
self.filteredSongs = SQLiteManager.getArtistSongs(artistID: self.artistID!, filter: filter)
} else if(self.playlist != nil) {
self.filteredSongs = SQLiteManager.getPlaylistSongs(playlist: self.playlist!, filter: filter)
}else {
self.filteredSongs = SQLiteManager.getSongs(filter: filter)
}
}
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if searchText == "" {
tableView.reloadData()
}
}
private func initSearchBar() {
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = false
searchController.searchResultsController?.view.isHidden = false
searchController.hidesNavigationBarDuringPresentation = false
self.extendedLayoutIncludesOpaqueBars = true
self.definesPresentationContext = true
searchController.searchBar.backgroundColor = UIColor.black
UIBarButtonItem.appearance(whenContainedInInstancesOf: [UISearchBar.self]).setTitleTextAttributes([NSAttributedStringKey.foregroundColor : UIColor.white], for: .normal)
self.navigationItem.titleView = searchController.searchBar
navigationItem.titleView?.isHidden = true
}
private func initNavigationBar() {
searchButton.tintColor = UIColor.white
if (fullListOfSongs()) {
searchButton.isEnabled = false
dataManager?.buttons.append(searchButton)
}
settingsButton.tintColor = UIColor.white
self.navigationItem.title = "Songs"
self.navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor : UIColor.white]
}
@IBAction func SearchButtonTapped(_ sender: Any) {
showSearchBar()
}
private func showSearchBar(){
self.navigationItem.titleView?.isHidden = false
self.searchController.isActive = true
DispatchQueue.main.asyncAfter(deadline: .now() + 0.2) {
self.searchController.searchBar.becomeFirstResponder()
}
navigationItem.rightBarButtonItems![0].isEnabled = false
navigationItem.rightBarButtonItems![0].image = nil
navigationItem.rightBarButtonItems![1].isEnabled = false
navigationItem.rightBarButtonItems![1].image = nil
}
private func hideSearchBar() {
navigationItem.titleView?.isHidden = true
navigationItem.rightBarButtonItems![0].isEnabled = true
navigationItem.rightBarButtonItems![0].image = UIImage(named: "settings")
navigationItem.rightBarButtonItems![1].isEnabled = true
navigationItem.rightBarButtonItems![1].image = UIImage(named: "search")
}
func isFiltering() -> Bool {
if(searchController == nil){
return false
}
return searchController.isActive && !isSearchBarEmpty()
}
private func isSearchBarEmpty() -> Bool {
return searchController.searchBar.text?.isEmpty ?? true
}
private func fullListOfSongs() -> Bool {
return self.playlist == nil && self.albumID == nil && self.artistID == nil
}
}
This worked when I tested. I believe the problem is in SongsTableViewController
: self.definesPresentationContext = false
. This should be true
for the pushed View Controller. (see docs here)
For SongsTableViewController
(pushed view controller) add the following:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.definesPresentationContext = true
}
And add this to AlbumsCollectionViewController
(initial view controller):
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.definesPresentationContext = false
}
这篇关于无法呈现 UISearchController的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!