I have a AVPlayerController that is embedded in a parentVC. Now if present the parentVC the normal way, using present(UIViewController, animated: ), everything works fine.
However, if I show the parentVC as a child controller of another controller, as shown below, the AVPlayer controls won't show. I can still tap on the center of the player to toggle video play, but can't see any controls.
How can I bring back the player controls? I've tried showsPlaybackControls not working either.
func present() {
guard let presenterView = presenter.view else {
return
}
presenter.addChild(self)
view.translatesAutoresizingMaskIntoConstraints = false
presenterView.addSubview(view)
setupExternalConstraints()
view.transform = CGAffineTransform(translationX: 0, y: presenter.view.frame.height)
UIView.animate(withDuration: 0.2, delay: 0, options: .curveEaseOut) {
self.view.transform = .identity
} completion: { _ in
self.didMove(toParent: presenter)
}
}
Related
I am a new Swift writer who is looking for an answer to a relatively specific question. Please forgive any novice mistakes.
I am trying to create a Pop Up on the screen programmatically that layers two view controllers so that one view controller is visible on the other, with a level of opacity that makes the background visible.
After a transition from my GameViewController see here:
let gameOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "gameOverID") as! GameOverViewController
self.addChildViewController(gameOverVC)
gameOverVC.view.frame = self.view.frame
self.view.addSubview(gameOverVC.view)
gameOverVC.didMove(toParentViewController: self)
}
... I instantiate a ViewController of GameOverViewController to fit over the GameViewController. After that I go to my GameOverViewController class and attempt to set the background color to be opaque with the following line of code:
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
The problem is that I end up with is a background that is not opaque, when in reality I would rather the GameViewController to layer on top of the GameViewController with an opaque background to still be able to see the Game.
I also animate with the following within my GameOverViewController:
func showAnimate()
{
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0
UIView.animate(withDuration: 0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
})
}
func removeAnimate()
{
UIView.animate(withDuration: 0.25, animations: {
self.view.transform = CGAffineTransform(scaleX: 1.3, y: 1.3)
self.view.alpha = 0.0
}, completion: { (finished: Bool) in
if (finished)
{
self.view.removeFromSuperview()
}
})
}
I'm not sure what is the source of my problem is.
Thank you for your help.
change background color of game over view Controller in interface builder like this
Also , take note of that you remove the game viewController's view in this line in removeAnimate func
self.view.removeFromSuperview()
this may cause black background if you do it before showing game over VC
for the view that sits on top, use: overFullScreen and then simply present it.
let topVC = topVC()
topVC.modalPresentationStyle = .overFullScreen
self.present(VCTobePresented, animated: true, completion: nil)
So, I've made a custom bottom bar with UIButton's. Now I am trying to replicate the fade in transition so when you click one of the buttons from the bottom bar will send to another view controller and show that view. I managed to do that but my navigation bar it's not a custom one and whenever I switch the views, the main view goes under the navigation bar.
I am using this code:
//MARK: Main view
func yourFoodGoalAction() -> Bool {
print("transition: YourFoodGoal")
let nextViewController = YourFoodGoal()
let toView = nextViewController.view
UIView.transition(from: view!, to: toView!, duration: 0.3, options: [.transitionCrossDissolve]) { (true) in
print("disolve")
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.title = "Your Goal"
})
}
return true
}
And to go back to the view (vice versa):
func selectedBarButtonAction() -> Bool {
print("transition: YourFoodGoal")
let nextViewController = YourFoodVC()
let toView = nextViewController.view
UIView.transition(from: view!, to: toView!, duration: 0.3, options: [.transitionCrossDissolve]) { (true) in
UIView.animate(withDuration: 0.3, animations: {
self.navigationItem.title = "Today's Meals"
})
}
return true
}
What should I do so my views will not go under the navigation bar once I change between them? I do not want to use container view as every view will be custom. Everything it's being made programmatically.
I found my answer. I was calling the modal transition wrong. Modals go full screen with the main view. I just embedded the navigation when I am presenting the new view and call the modal from there:
func todaysMealsAction() {
let yourFoodVC = YourFoodVC()
yourFoodVC.modalTransitionStyle = UIModalTransitionStyle.crossDissolve
let navBarOnModal: UINavigationController = UINavigationController(rootViewController: yourFoodVC)
self.present(navBarOnModal, animated: false, completion: nil)
}
I have run into an issue when using a custom segue. I have two tableviews that I'm an trying to switch back and forth from. When I click on a cell in tableview1 it should take me to tableview2. I have a button on tableview2 that connects to the exit of the storyboard. From there it should take me back to tableview1 but whenever I press the button, the application crashes with a BAD_ACCESS error.
Here is my custom segue class:
class TableViewSegue: UIStoryboardSegue {
override func perform() {
scale()
}
func scale () {
let toViewcontroller = self.destination
let fromViewcontroller = self.source
let containerView = fromViewcontroller.view.superview
let originalCenter = fromViewcontroller.view.center
toViewcontroller.view.transform = CGAffineTransform(scaleX: 0.05, y: 0.05)
toViewcontroller.view.center = originalCenter
containerView?.addSubview(toViewcontroller.view)
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
toViewcontroller.view.transform = CGAffineTransform.identity
}, completion: { success in
fromViewcontroller.present(toViewcontroller, animated: false, completion: nil) //The application crashes and highlights this line as the error.
})
}
}
I have implemented this method in my tableViewController1:
#IBAction func prepareForUnwind(segue: UIStoryboardSegue) {
}
Not sure why the tableview2 does not dismiss.
EDIT: The issue had to do with needing a navigation controller.
The problem is that you are presenting the toViewcontroller each time a segue is performed. So the app presents table2 over table1, and then tries again to present table1 over table2 on the unwind.
Modify your custom segue to check - essentially - which direction you're going:
class TableViewSegue: UIStoryboardSegue {
override func perform() {
scale()
}
func scale () {
let toViewcontroller = self.destination
let fromViewcontroller = self.source
let containerView = fromViewcontroller.view.superview
let originalCenter = fromViewcontroller.view.center
toViewcontroller.view.transform = CGAffineTransform(scaleX: 0.05, y: 0.05)
toViewcontroller.view.center = originalCenter
containerView?.addSubview(toViewcontroller.view)
let fromP = fromViewcontroller.presentingViewController
UIView.animate(withDuration: 0.5, delay: 0, options: .curveEaseInOut, animations: {
toViewcontroller.view.transform = CGAffineTransform.identity
}, completion: { success in
// if nil, we are presenting a new VC
if fromP == nil {
fromViewcontroller.present(toViewcontroller, animated: false, completion: nil)
} else {
fromViewcontroller.dismiss(animated: false, completion: nil)
}
})
}
}
Note: This is assuming:
you are not trying to push/pop within a UINavigationController ... you'd need to add some other checks to handle that.
you are only going one-level-in, that is, you are not presenting, presenting, presenting, etc. and then trying to unwind.
func displayPopover() {
let popController = UIViewController()
popController.view.backgroundColor = .red
// set up the popover presentation controller
popController.modalPresentationStyle = .popover
popController.popoverPresentationController?.permittedArrowDirections = UIPopoverArrowDirection.up
popController.popoverPresentationController?.delegate = self
popController.popoverPresentationController?.sourceView = self.view
popController.popoverPresentationController?.sourceRect = CGRect(x: 100, y: 100, width: 100, height: 100)
// present the popover
self.present(popController, animated: true, completion: nil)
}
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController!) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .none
}
It displays the popover like any other view controller, sliding from the bottom displaying a red screen.
To get the "popup" effect:
1.You should make viewController in storyboard and then add view inside it like this(also add constraints):
2.You should declare the popover like this:
let popOverVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewControllerWithIdentifier("popupID") as! PopUpViewController
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMoveToParentViewController(self)
3.Now you should make the background darker and transparent like this self.view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.8) in viewDidLoad()
4.And to open it(it also animates it slightly):
self.view.transform = CGAffineTransformMakeScale(1.3, 1.3)
self.view.alpha = 0.0;
UIView.animateWithDuration(0.25, animations: {
self.view.alpha = 1.0
self.view.transform = CGAffineTransformMakeScale(1.0, 1.0)
});
As per documentation
In a horizontally regular environment, a presentation style where the content is displayed in a popover view. The background content is dimmed and taps outside the popover cause the popover to be dismissed. If you do not want taps to dismiss the popover, you can assign one or more views to the passthroughViews property of the associated UIPopoverPresentationController object, which you can get from the popoverPresentationController property.
In a horizontally compact environment, this option behaves the same as fullScreen.
For iPhones popOver will behave same like fullscreen and for iPad it will be displayed in a popover view.
I'm trying to animate up a UICollectionViewController over an original UIViewController with the original UIViewController blurred as background, however, every time the animation finishes, I can see clearly through the blurred view that the original view controller is dismissed, what do I do to avoid the first UIViewController being dismissed?
Code in the first view controller to present the second:
let VC = storyboard?.instantiateViewController(withIdentifier: "PopoverCollectionVC") as! PopoverCollectionVC
VC.setDataSource(with: .calcDPSItems)
VC.collectionView?.backgroundColor = UIColor.clear
VC.transitioningDelegate = self
self.present(VC, animated: true, completion: nil)
Code in the animator object for custom animation:
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let containerView = transitionContext.containerView
let fromView = transitionContext.view(forKey: .from)!
let toView = transitionContext.view(forKey: .to)!
if presenting {
// configure blur
effectView.frame = fromView.bounds
containerView.addSubview(effectView)
// configure collection view
toView.frame = CGRect(x: 0, y: fromView.frame.height, width: fromView.frame.width, height: fromView.frame.height / 2)
containerView.addSubview(toView)
UIView.animateKeyframes(withDuration: duration, delay: 0, options: .calculationModeCubic, animations: {
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.6) {
toView.center.y = fromView.center.y
}
UIView.addKeyframe(withRelativeStartTime: 0.1, relativeDuration: 1) {
self.effectView.effect = UIBlurEffect(style: .dark)
}
}) { _ in
transitionContext.completeTransition(true)
}
} else {
...
}
You need to specify that the modal presentation style will be over the current context :
VC.modalPresentationStyle = .overCurrentContext
Then to get the the presented view you need
// Get the from view from The ViewController because there is a bug in iOS when
// using some modalPresentationStyle values
// that causes the viewForKey to returm nil for UITransitionContextFromViewKey
// www.splinter.com.au/2015/04/17/ios8-view-controller-transitioning-bug/
let fromVC = transitionContext.viewController(forKey: .from)
let fromView = fromVC?.view
You should instead take a snapshot of the previous view, then blur than and use it as a background on the new VC.
let oldView = view_to_copy.snapshotView(afterScreenUpdates: false)
Then blur this, and add as a subview to your collectionView controller.
EDIT:
Just to add if you are using the VC inside something like UITabbar or UINavigationController you may need to snapshot that view to ensure all the UI is inside the snapshot.