我有以下视图层次结构:
UINavigationController
||
\/
LibraryTableViewController: UITableViewController
||
\/
AlbumsCollectionViewController: UICollectionViewController
||
\/
SongsTableViewController: UITableViewController
我想在AlbumsCollectionViewController
中设置一个搜索栏,在SongsTableViewController
中显示navigationItem.titleView
中显示的另一个搜索栏。
我设法在AlbumsCollectionViewController
中添加了一个工作搜索栏,如下所示:
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
}
}
请注意,搜索栏隐藏在ViewDidLoad()
上,并在按下按钮时显示,如SearchButtonTapped
方法所示。
现在,我正尝试在SongsTableViewController
中执行相同操作,但是,点击按钮(即调用SearchButtonTapped
)时搜索栏未显示,我收到以下消息:
Warning: Attempt to present <UISearchController: 0x7f8158812b50> on <MyProject.AlbumsCollectionViewController: 0x7f81588023c0> whose view is not in the window hierarchy!
如果我评论了行searchController.isActive = true
,那么搜索栏会显示,但即使我点击它也不会有效。
修改
很抱歉,如果我还不清楚的话。我在UISearchController
中有一个单独的SongsTableViewController
。我的意思是我在两个控制器中使用相同的逻辑
另请注意,如果我从导航控制器推送SongsTableViewController
(即视图层次结构只有2个控制器(UINavigationController =&gt; SongsTableViewController),搜索栏就可以正常工作
这是SongsTableViewController
的大部分代码(省略不相关的内容)
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
}
}
答案 0 :(得分:1)
这在我测试时起作用了。我认为问题出在SongsTableViewController
:self.definesPresentationContext = false
。对于推送的View Controller,这应该是true
。 (参见文档here)
对于SongsTableViewController
(推送视图控制器),添加以下内容:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.definesPresentationContext = true
}
并将其添加到AlbumsCollectionViewController
(初始视图控制器):
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.definesPresentationContext = false
}
答案 1 :(得分:0)
当您在SongsTableViewController
时,您的AlbumsCollectionViewController
不在窗口层次结构中。
我可以理解,您从showSearchBar
拨打AlbumsCollectionViewController
SongsTableViewController
方法。由于您从AlbumsCollectionViewController
导航到SongsTableViewController
,因此您的AlbumsCollectionViewController
不在窗口层次结构中,因此无法显示搜索控制器。
要修复尝试在SongsTableViewController
中添加单独的搜索栏控制器,就像之前在AlbumsCollectionViewController
中所做的那样。
或者,您可以创建一个单独的视图控制器,实现搜索功能,然后从两个控制器中显示它。