My project is created programmatically without using storyboard. And it is like Apple Music's miniPlayer, when clicking a row in tableView, will update the data of miniPlayer(which is in containerView).
I see some examples with storyboard and segue like below code: call child viewController's method in parent viewController to update data by using protocol & delegate.
But I don't use storyboard, so what is the alternative code to 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
}
}
My Code: add container view in the root view controller(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)
}
}
I want to trigger the update when clicking the row in 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)
}
The update method in child view.
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")
}
}
}
There is more than one approach to this...
First approach - no custom delegate:
Use the subclassed
UITabBarControlleras an "intermediary". Give it a func such as:then, in your "Select Song" view controller (one of the tabs):
Second approach - using a custom delegate:
Make sure your "mini player" controller conforms to the delegate:
Give your "Select Song" view controller (and any other of the tab controllers) a delegate var:
then, in your subclassed
UITabBarController:now your "Select Song" view controller can call the delegate func: