I have a popover view (without a tab bar) that is popped over to a view controller with a tab bar. In the view controller with a tab bar I set up a button to click, so that the view controller pops up:
#IBAction func PopUpClicked(_ sender: UIButton) -> Void {
let popOverVC = UIStoryboard(name: "SpinningWheel", bundle: nil).instantiateViewController(withIdentifier: "PhotoPopUp") as! PopUpViewController
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.frame
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: self)
}
And in the popOver view controller, i animated the pop over.
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.8)
self.showAnimate()
// Do any additional setup after loading the view.
}
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()
}
});
}
But when the popover happens, Everything has a faded black background, which I want except for the tab bar. I would like for the pop over View to also pop over the tabbar and for the faded black background to go over it.
This is what I want it to look like:
With the black faded background covering the tabbar
This happens, because you have to present your popover as a modal ViewController. To achieve this you have to set the modal presentation style before presenting your popover from your target ViewController. This code should be called in your presenting ViewController:
let vc = YourPopOverViewController(nib: UINib(name: "PopOverViewController", bundle: nil), bundle: nil)
vc.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
tabBarController.present(vc, animated: true)
EDIT:
This should do the trick if you have designed your PopOverViewController as a fullscreen ViewController. I have done this a bunch of times and have left the space which should not be presented as clear background:
#IBAction func PopUpClicked(_ sender: UIButton) -> Void {
let popOverVC = UIStoryboard(name: "SpinningWheel", bundle: nil).instantiateViewController(withIdentifier: "PhotoPopUp") as! PopUpViewController
popOverVc.modalPresentationStyle = UIModalPresentationStyle.OverCurrentContext
tabBarController.present(popOverVC, animated: true)
}
This 3rd party EzPopup can resolve the problem easily: https://github.com/huynguyencong/EzPopup
// init YourViewController
let contentVC = ...
// Init popup view controller with content is your content view controller
let popupVC = PopupViewController(contentController: contentVC, popupWidth: 100, popupHeight: 200)
// show it by call present(_ , animated:) method from a current UIViewController
present(popupVC, animated: true)
Related
I am showing a custom pop up over my main viewcontroller. For this I have created a viewcontroller in the storyboard (image shown), the corresponding class being as below.
class PopUpViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
self.showAnimate()
}
func showAnimate()
{
self.view.alpha = 1.0
}
func removeAnimate()
{
UIView.animate(withDuration: 0.0, animations: {
self.view.alpha = 0.0;
}, completion:{(finished : Bool) in
if (finished) {
self.view.removeFromSuperview()
}
});
}
}
Then in my main viewcontroller, I show this pop up on a button click as follows:
let popOverVC = UIStoryboard(name: "MainViewController", bundle: nil).instantiateViewController(withIdentifier: "popup") as! PopUpViewController
self.addChildViewController(popOverVC)
popOverVC.view.frame = self.view.bounds
self.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: self)
This makes the background of the main view controller black with opacity of 70% when the pop up is added. How can I make the navigation bar also have the same background effect?
I have tried updating:
self.view.window?.backgroundColor = UIColor.black.withAlphaComponent(0.7)
and
self.navigationController?.navigationBar.backgroundColor = UIColor.black.withAlphaComponent(0.7)
in viewDidLoad() but did not work. Any possible solution?
If I understand it correctly, you are adding the popOverVC as a subview to the view of your mainViewController that is embedded in UINavitationController. If that is the case, then it is only logical that the popOverVC does not overlay the navigationBar, because navigationBar is a subview of the navigationController, not of your mainViewController. To be able to overlay also the navigationBar, you will have to add that popOverVC to the navigationController:
// to make things a bit easier working with the optional self.navigationController
guard let navController = self.navigationController else { return }
let popOverVC = UIStoryboard(name: "MainViewController", bundle: nil).instantiateViewController(withIdentifier: "popup") as! PopUpViewController
navController.addChildViewController(popOverVC)
popOverVC.view.frame = navController.view.bounds
navController.view.addSubview(popOverVC.view)
popOverVC.didMove(toParentViewController: navController)
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 have 2 View Controllers and a Navigation Controller. When the screen is clicked, the 1st VC segues to the 2nd VC, and there is a back button for the segue to unwind to go back to the 1st VC.
I did not like the vertical animation of the segues, so (with some help) I created custom, horizontal animations.
The 1st VC works great with the animation sliding from right to left. But once that is done, the 2nd VC does not want to unwind (2nd VC should go from left to right).
I do get this Warning..
Warning: Attempt to present UINavigationController: 0x7fce2082b000 on TestApp.HomeViewController: 0x7fce20410030 whose view is not in the window hierarchy!
Also, if I take the script from the 1st VC segue, then I am able go unwind from the 2nd VC w/ the proper animation.
Here's the code for the segues:
1st VC
#IBAction func performSegue(_ sender: Any) {
if shouldPerformSegue(withIdentifier: "segue", sender: Any?.self) {
performSegue(withIdentifier: "segue", sender: nil)
}
}
#IBAction func unwindToHomeView(segue: UIStoryboardSegue) {
}
override func unwind(for unwindSegue: UIStoryboardSegue, towardsViewController subsequentVC: UIViewController) {
let segue = SegueFromLeft(identifier: unwindSegue.identifier, source: unwindSegue.source, destination: unwindSegue.destination)
segue.perform()
}
Right to Left Animation
let dst = self.destination
let src = self.source
let containerView = src.view.superview
dst.view.transform = CGAffineTransform(translationX: src.view.frame.size.width, y: 0)
containerView?.addSubview(dst.view)
UIView.animate(withDuration: 0.25, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
}, completion: { success in
src.present(dst, animated: false, completion: nil)
})
Left to Right Animation
let dst = self.destination
let src = self.source
src.view.superview?.insertSubview(dst.view, at: 0)
UIView.animate(withDuration: 0.25, delay: 0.0, options: UIViewAnimationOptions.curveEaseInOut, animations: {
src.view.transform = CGAffineTransform(translationX: -src.view.frame.size.width, y: 0)
}, completion: { success in
src.dismiss(animated: false, completion: nil)
})
Any help would be greatly appreciated.
You can simply push viewcontroller to navigation controllers where you don't need to explicitly handle left right animation. In VC1 navigationController?.pushViewController(VC2, animated: true) will simply animate VC2 from right to left with handy back button which can be used to animate VC2 left to right displaying VC1.
For my solution I actually did away with any "custom" animations, and fixed my issues mostly just by moving the navigation controller as the initial controller, attached to the VC1.
From there I just pushed to the next view (VC2), and used unwind segue to go back (to VC1).
Basic code for pushing:
let vcName = "Main"
let viewController = storyboard?.instantiateViewController(withIdentifier: vcName)
self.navigationController?.pushViewController(viewController!, animated: true)
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 developing an ios app. I have a a main view and in this view
im trying to present a modal view controller with dimmed background(black with opacity).
The problem is that the status bar is not affected by this color and remains the same.
This is how i present the view controller:
let shareViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ShareViewController") as! ShareViewController
shareViewController.battle = battle
shareViewController.delegate = self
let animation = CATransition()
animation.duration = 1
animation.type = kCATransitionFade
self.view.window?.layer.addAnimation(animation, forKey: kCATransition)
presentViewController(shareViewController, animated: false) {
() in
// nothing here
}
Here are some screenshots to demonstrate the problem:
This is the problem(status bar color):
Problem illustration
This is the modal view in storyboard:
storyboard
I cannot reproduce your problem, the following code works without problems in my single view app:
let viewController = UIViewController()
viewController.modalPresentationStyle = .overFullScreen
viewController.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
let animation = CATransition()
animation.duration = 1
animation.type = kCATransitionFade
self.view.window?.layer.add(animation, forKey: kCATransition)
self.present(viewController, animated: false, completion: nil)
However note that you should be presenting over the root controller of the view. Sometimes you can get strange effects when presenting from your internal controllers:
self.view.window?.rootViewController?.present(viewController, animated: false, completion: nil)
Also make sure you are using the correct modalPresentationStyle.
Set your view controller as the root view controller of a UIWindow, then present the window at the UIWindowLevelAlert level.
Below is a Swift 3 class used to animate a modal popup over all other UI elements, including the status bar. A scrim view is used to shade background UI and intercept touches to dismiss the view.
import UIKit
class ModalViewController: UIViewController {
private let scrimView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.black
view.alpha = 0.0
return view
}()
private var myWindow: UIWindow?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.clear
// Setup scrim View
view.addSubview(scrimView)
view.topAnchor.constraint(equalTo: scrimView.topAnchor).isActive = true
view.leftAnchor.constraint(equalTo: scrimView.leftAnchor).isActive = true
view.rightAnchor.constraint(equalTo: scrimView.rightAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: scrimView.bottomAnchor).isActive = true
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(dismiss as (Void) -> Void))
scrimView.addGestureRecognizer(tapGestureRecognizer)
// Layout custom popups or action sheets
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
UIView.animate(withDuration: 0.25) {
self.scrimView.alpha = 0.5
// Animate in custom popups or action sheets
}
}
func present() {
myWindow = UIWindow(frame: UIScreen.main.bounds)
myWindow?.windowLevel = UIWindowLevelAlert
myWindow?.backgroundColor = UIColor.clear
myWindow?.rootViewController = self
myWindow?.isHidden = false
}
func dismiss() {
UIView.animate(
withDuration: 0.25,
animations: {
self.scrimView.alpha = 0.0
// Animate out custom popups or action sheets
},
completion: { success in
self.myWindow = nil
}
)
}
}
To present the view:
let modalView = ModalViewController()
modalView.present()
To dismiss the view, tap anywhere on the scrim.
this code works for me, when I am presenting UIViewController with alpha != 1. present UIViewController like:
let storyBoard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyBoard.instantiateViewController(withIdentifier: "AddComment") as! AddCommentViewController
destinationVC.modalPresentationStyle = .overCurrentContext //this line is important
destinationVC.delegate = self
destinationVC.restId = self.restaurant.id
self.present(destinationVC, animated: true, completion: nil)
then in destinationVC view controller
override func viewWillDisappear(_: Bool) {
UIView.animate(withDuration: 1, animations: { () in
self.view.backgroundColor = .clear
})
super.viewWillDisappear(true)
}
override func viewWillAppear(_: Bool) {
UIView.animate(withDuration: 1, animations: { () in
self.view.backgroundColor = UIColor.black.withAlphaComponent(0.5)
})
super.viewWillAppear(true)
}
and set its backgroundColor to .clear in viewDidLoad or storyboard. So UIViewController covers whole screen including status bar.
Here is the solution you might be looking for:
if let window = UIApplication.shared.keyWindow {
window.windowLevel = UIWindowLevelStatusBar + 1
}
The main idea behind this code is, window of your application has a window level which is lower than status bar window level. And what this code does is, just put your window's window level higher than status bar window level, and your window can now cover the status bar. Don't forget, this code has to be called on main thread, just before presenting your view controller. Good luck!
Custom animation transitions should be performed using UIViewControllerAnimatedTransitioning. Here is a tutorial for this purpose:
https://www.raywenderlich.com/110536/custom-uiviewcontroller-transitions
If all you want is a fade animation you can have it by changing the modalTransitionStyle property of the viewController you are going to display.
Try by fixing your code this way:
guard let shareViewController = self.storyboard?.instantiateViewControllerWithIdentifier("ShareViewController") as! ShareViewController else {
//Fallback in case of nil?
return
}
shareViewController.modalTransitionStyle = .crossDissolve
presentViewController(shareViewController, animated: true, completion: nil)
Also please note that presentViewController(shareViewController, animated: true, completion: nil) is for swift 2. The equivalent swift 3 would be present(shareViewController, animated: true, completion: nil)
you can add this code to view controller for Swift 3:
let statusView: UIView = UIView(frame: CGRect(x: 0.0, y: -20.0, width: UIScreen.main.bounds.size.width, height: 20.0))
statusView.backgroundColor = UIColor.black
statusView.alpha = 0.8
self.addSubview(self.statusView)
You could be extremely practical and simply hide the status bar when your modal view controller is up:
override func prefersStatusBarHidden() -> Bool {
return true
}