Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
169 views
in Technique[技术] by (71.8m points)

ios - Swift: How to update data in Container View without storyboard

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")
        }
    }
}
question from:https://stackoverflow.com/questions/65904480/swift-how-to-update-data-in-container-view-without-storyboard

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There is more than one approach to this...


First approach - no custom delegate:

Use the subclassed UITabBarController as an "intermediary". Give it a func such as:

func configure(songs: [Song]?, at index: Int) -> Void {
    miniPlayer.configure(songs: songs, at: index)
}

then, in your "Select Song" view controller (one of the tabs):

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)
}

Second approach - using a custom delegate:

protocol ContentDelegate {
    func configure(songs: [Song]?, at index: Int)
}

Make sure your "mini player" controller conforms to the delegate:

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")
        }
    }

}

Give your "Select Song" view controller (and any other of the tab controllers) a delegate var:

class SelectSongViewController: UIViewController {

    var delegate: ContentDelegate?

    // everything else
}

then, in your subclassed UITabBarController:

override func viewDidLoad() {
    super.viewDidLoad()

    configureContainer()

    if let vc = viewControllers?.first as? SelectSongViewController {
        vc.delegate = miniPlayer
    }
    
}

now your "Select Song" view controller can call the delegate func:

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)
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...