Reputation: 271584
I have a splashViewController
with a containerView
, which is created in Storyboard.
In my story board, I automatically drag an embed segue from my containerView
to a profileViewController
.
Inside my splashViewController
, I want to programatically "destroy" the containerView
+ profileViewController
(both of them).
I've tried this:
self.containerView.hidden = true //obviously doesn't work. it's just visual
self.containerView.removeFromSuperView() //nope. it's just visual.
How can I remove both containerView and profileViewController completely, making sure that both deinit
appropriately? If that's not possible, can I at least deinit the profileViewController? (I'll just set containerView as hidden).
Note: I play a movie video automatically (looping) in my profileViewController. It has sound, and even when I set containerView to nil and remove it from superview, the sound keeps playing.
Upvotes: 1
Views: 2151
Reputation: 17536
I wrote up a quick example project here demonstrating the solution here.
An autoplaying video (with dog barking) automatically plays in the bottom of the screen. Pressing the "Stop Player" button removes the child UIViewController
and the container.
MPMoviePlayerController
finished playing the audio even after the moviePlayer was stopped or set to nil (although not necessarily deallocated if an internal part of the MPMoviePlayerController
held a strong reference which I suspect was the problem).
Anyway, I found a workaround by setting the contentURL
of the movie player to nil on deinit
before calling moviePlayer.stop()
//The `ProfileViewController` is created in the storyboard as a child of `ViewController`.
class ViewController: UIViewController {
@IBOutlet weak var container: UIView?
@IBAction func stopPlayer(sender: UIButton) {
//We remove the child view controller (the ProfileViewController) from the parent
for controller in self.childViewControllers {
if let child = controller as? ProfileViewController {
child.removeFromParentViewController() //This causes the deinit call
container?.removeFromSuperview() //Clear up the container (stays as a black box otherwise)
}
}
}
}
In the ProfileViewController
, we have to change the contentURL and stop our movie player
class ProfileViewController: UIViewController {
lazy var moviePlayer: MPMoviePlayerController = {
let path = NSBundle.mainBundle().pathForResource("TestMovie", ofType: "mp4")!
let moviePlayer = MPMoviePlayerController(contentURL: NSURL(fileURLWithPath: path)!)
moviePlayer.movieSourceType = .File
moviePlayer.prepareToPlay()
moviePlayer.view.frame = self.view.frame
return moviePlayer
}()
override func viewDidAppear(animated: Bool) {
view.addSubview(moviePlayer.view)
moviePlayer.play()
//Looping
NSNotificationCenter.defaultCenter().addObserver(self, selector: "moviePlayerDidFinish:", name:MPMoviePlayerPlaybackDidFinishNotification, object: moviePlayer)
}
//Change our contentURL then stop
deinit {
moviePlayer.contentURL = nil
moviePlayer.stop()
}
@objc func moviePlayerDidFinish(notification: NSNotification) {
let reasonInt = notification.userInfo![MPMoviePlayerPlaybackDidFinishReasonUserInfoKey] as! Int
let reason = MPMovieFinishReason(rawValue: reasonInt)
if (reason == MPMovieFinishReason.PlaybackEnded) {
moviePlayer.play()
}
}
}
Tested on a iPhone 5 device and on the simulator
UIView
itself by calling removeFromSuperview
MPMoviePlayerController
keeps playing audio after being stopped, this is a bug. Workaround it by setting the contentURL then stoppingUpvotes: 3
Reputation: 4583
There are some details missing here, so I'll try to do my best, but it sounds like containerView is not the main view of that UIViewController, as I don't think you can say self.view.removeFromSuperView()
If you want to have a pattern where you delete the container view, I would recommend taking a look at how Apple suggests that you do this with their ContainerViewController There are a few more steps involved but it allows you to easily add and remove children. The downside is that you need to have a ViewController associated with each view.
The issue that it sounds like you are dealing with is this MPMoviePlayerController. MPMoviePlayerController requires a view associated with it. From Apple's documentation
Playback occurs in a view owned by the movie player and takes place either fullscreen or inline. You can incorporate a movie player’s view into a view hierarchy owned by your app, or use an MPMoviePlayerViewController object to manage the presentation for you.
My guess would be without looking at any code, that MPMoviePlayerController is hanging onto a reference to one of the view's that your deleting and its a strong reference so it isn't letting you actually delete that view and causing it to continue without being on screen.
It looks like MPMoviePlayerController is deprecated in ios9 so you should move to AVPlayer, which should give you more control over a movie inside your view
Upvotes: 1
Reputation: 4016
I think you should find a way to stop playing sounds. Also, you can just assign nil
to those view controllers you want to deallocate. However, you can not do self = nil;
.
Upvotes: -1