Swift: 如何在没有故事板的情况下更新容器视图中的数据
Swift: How to update data in Container View without storyboard
我的项目是在不使用故事板的情况下以编程方式创建的。又像Apple Music的miniPlayer,在tableView中点击一行,会更新miniPlayer(在containerView中)的数据。
我看到了一些故事板和 segue 的示例,如下面的代码:在父 viewController 中调用子 viewController 的方法以使用协议和委托更新数据。
但我不使用情节提要,那么 prepare()
的替代代码是什么?
protocol ContentDelegate {
func updateContent(id: Int)
}
class ParentViewController: UIViewController {
var delegate: ContentDelegate?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "containerController") {
let containerVC = segue.destination as! ChildContainerViewController
self.delegate = containerVC
}
}
}
class ChildContainerViewController: UIViewController, ContentDelegate {
func updateContent(id: Int) {
// your code
}
}
我的代码:在根视图控制器(UITabViewController)中添加容器视图。
class ViewController: UITabBarController {
// mini player
var miniPlayer: MiniPlayerViewController?
// container view
var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// set tabBar and other stuff
...
configureContainer()
}
func configureContainer() {
// add container
containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: tabBar.topAnchor),
containerView.heightAnchor.constraint(equalToConstant: 64.0)
])
// add child view controller view to container
miniPlayer = MiniPlayerViewController()
guard let miniPlayer = miniPlayer else { return }
addChild(miniPlayer)
miniPlayer.view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(miniPlayer.view)
// Create and activate the constraints for the child’s view.
guard let miniPlayerView = miniPlayer.view else { return }
NSLayoutConstraint.activate([
miniPlayerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
miniPlayerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
miniPlayerView.topAnchor.constraint(equalTo: containerView.topAnchor),
miniPlayerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
])
miniPlayer.didMove(toParent: self)
}
}
我想在点击 parentView 中的行时触发更新。
protocol ContentDelegate {
func configure(songs: [Song]?, at index: Int)
}
class SongsListViewController: UIViewController {
private var tableView: UITableView!
var delegate: ContentDelegate?
// MARK: - data source
var songs = [Song]()
. . .
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = indexPath.row
let vc = MiniPlayerViewController()
self.delegate = vc
self.delegate?.configure(songs: songs, at: index)
// present(vc, animated: true)
}
子视图中的更新方法。
extension MiniPlayerViewController {
func configure(songs: [Song]?, at index: Int) {
if let songs = songs {
let song = songs[index]
songTitle.text = song.title
thumbImage.image = song.artwork?.image
} else {
// placeholder fake info
songTitle.text = "你在终点等我"
thumbImage.image = UIImage(named: "Wang Fei")
}
}
}
解决这个问题的方法不止一种...
第一种方法 - 无自定义委托:
使用子类 UITabBarController
作为“中介”。给它一个函数,例如:
func configure(songs: [Song]?, at index: Int) -> Void {
miniPlayer.configure(songs: songs, at: index)
}
然后,在您的“Select 歌曲”视图控制器(选项卡之一)中:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let tbc = self.tabBarController as? CustomTabBarController else {
return
}
let index = indexPath.row
tbc.configure(songs: songs, at: index)
}
第二种方法 - 使用自定义委托:
protocol ContentDelegate {
func configure(songs: [Song]?, at index: Int)
}
确保您的“迷你播放器”控制器符合委托:
class MiniPlayerViewController: UIViewController, ContentDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// add UI elements, any other setup code
}
}
extension MiniPlayerViewController {
func configure(songs: [Song]?, at index: Int) {
if let songs = songs {
let song = songs[index % songs.count]
songTitle.text = song.title
thumbImage.image = song.artwork
} else {
// placeholder fake info
songTitle.text = "你在终点等我"
thumbImage.image = UIImage(named: "Wang Fei")
}
}
}
给你的“Select Song”视图控制器(以及任何其他选项卡控制器)一个委托变量:
class SelectSongViewController: UIViewController {
var delegate: ContentDelegate?
// everything else
}
然后,在你的子类中 UITabBarController
:
override func viewDidLoad() {
super.viewDidLoad()
configureContainer()
if let vc = viewControllers?.first as? SelectSongViewController {
vc.delegate = miniPlayer
}
}
现在您的“Select 歌曲”视图控制器可以调用委托函数:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let tbc = self.tabBarController as? CustomTabBarController else {
return
}
let index = indexPath.row
delegate?.configure(songs: songs, at: index)
}
我的项目是在不使用故事板的情况下以编程方式创建的。又像Apple Music的miniPlayer,在tableView中点击一行,会更新miniPlayer(在containerView中)的数据。
我看到了一些故事板和 segue 的示例,如下面的代码:在父 viewController 中调用子 viewController 的方法以使用协议和委托更新数据。
但我不使用情节提要,那么 prepare()
的替代代码是什么?
protocol ContentDelegate {
func updateContent(id: Int)
}
class ParentViewController: UIViewController {
var delegate: ContentDelegate?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "containerController") {
let containerVC = segue.destination as! ChildContainerViewController
self.delegate = containerVC
}
}
}
class ChildContainerViewController: UIViewController, ContentDelegate {
func updateContent(id: Int) {
// your code
}
}
我的代码:在根视图控制器(UITabViewController)中添加容器视图。
class ViewController: UITabBarController {
// mini player
var miniPlayer: MiniPlayerViewController?
// container view
var containerView: UIView!
override func viewDidLoad() {
super.viewDidLoad()
// set tabBar and other stuff
...
configureContainer()
}
func configureContainer() {
// add container
containerView = UIView()
containerView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(containerView)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
containerView.leadingAnchor.constraint(equalTo: g.leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: g.trailingAnchor),
containerView.bottomAnchor.constraint(equalTo: tabBar.topAnchor),
containerView.heightAnchor.constraint(equalToConstant: 64.0)
])
// add child view controller view to container
miniPlayer = MiniPlayerViewController()
guard let miniPlayer = miniPlayer else { return }
addChild(miniPlayer)
miniPlayer.view.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(miniPlayer.view)
// Create and activate the constraints for the child’s view.
guard let miniPlayerView = miniPlayer.view else { return }
NSLayoutConstraint.activate([
miniPlayerView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
miniPlayerView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
miniPlayerView.topAnchor.constraint(equalTo: containerView.topAnchor),
miniPlayerView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
])
miniPlayer.didMove(toParent: self)
}
}
我想在点击 parentView 中的行时触发更新。
protocol ContentDelegate {
func configure(songs: [Song]?, at index: Int)
}
class SongsListViewController: UIViewController {
private var tableView: UITableView!
var delegate: ContentDelegate?
// MARK: - data source
var songs = [Song]()
. . .
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let index = indexPath.row
let vc = MiniPlayerViewController()
self.delegate = vc
self.delegate?.configure(songs: songs, at: index)
// present(vc, animated: true)
}
子视图中的更新方法。
extension MiniPlayerViewController {
func configure(songs: [Song]?, at index: Int) {
if let songs = songs {
let song = songs[index]
songTitle.text = song.title
thumbImage.image = song.artwork?.image
} else {
// placeholder fake info
songTitle.text = "你在终点等我"
thumbImage.image = UIImage(named: "Wang Fei")
}
}
}
解决这个问题的方法不止一种...
第一种方法 - 无自定义委托:
使用子类 UITabBarController
作为“中介”。给它一个函数,例如:
func configure(songs: [Song]?, at index: Int) -> Void {
miniPlayer.configure(songs: songs, at: index)
}
然后,在您的“Select 歌曲”视图控制器(选项卡之一)中:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let tbc = self.tabBarController as? CustomTabBarController else {
return
}
let index = indexPath.row
tbc.configure(songs: songs, at: index)
}
第二种方法 - 使用自定义委托:
protocol ContentDelegate {
func configure(songs: [Song]?, at index: Int)
}
确保您的“迷你播放器”控制器符合委托:
class MiniPlayerViewController: UIViewController, ContentDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// add UI elements, any other setup code
}
}
extension MiniPlayerViewController {
func configure(songs: [Song]?, at index: Int) {
if let songs = songs {
let song = songs[index % songs.count]
songTitle.text = song.title
thumbImage.image = song.artwork
} else {
// placeholder fake info
songTitle.text = "你在终点等我"
thumbImage.image = UIImage(named: "Wang Fei")
}
}
}
给你的“Select Song”视图控制器(以及任何其他选项卡控制器)一个委托变量:
class SelectSongViewController: UIViewController {
var delegate: ContentDelegate?
// everything else
}
然后,在你的子类中 UITabBarController
:
override func viewDidLoad() {
super.viewDidLoad()
configureContainer()
if let vc = viewControllers?.first as? SelectSongViewController {
vc.delegate = miniPlayer
}
}
现在您的“Select 歌曲”视图控制器可以调用委托函数:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
guard let tbc = self.tabBarController as? CustomTabBarController else {
return
}
let index = indexPath.row
delegate?.configure(songs: songs, at: index)
}