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
}
Related
I want to make the screen like the following.
In source class, I write this code and push to side menu controller.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "SidemenuController")as! SidemenuController
self.navigationController?.pushViewController(vc, animated: false)
I write the following code in destination controller view did load method but just color opacity is decreased data is not visible.
override func viewDidLoad() {
self.view.backgroundColor = self.hexStringToUIColor(hex: "BE8790").withAlphaComponent(0.4)
}
Instead of using .withAlphaComponent(0.4)
Try this Adjust the number as needed
self.view.alpha = 0.5
You show present your SideViewController. Maybe custom how it shows
let sideVC = // Your init
sideVC.modalPresentationStyle = .fullScreen
present(sideVC, animated: true, completion: nil)
In your SideViewController,
override func viewDidLoad() {
super.viewDidLoad()
view.alpha = 0.5
view.backgroundColor = // Your color
tableView.backgroundColor = .clear
}
When going back from view controller with:
navigationItem.largeTitleDisplayMode = .never
to view controller with:
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .always
in viewDidLoad I have a strange animation glitch on navigation bar - a white stripe appears
It's appears only when searchBar is visible
How can I fix this?
I found problem in appear transition, there are some clear space between navigationbar and searchbar. They have independent animation. Here is it.
So, you can add common background for them throw transition in "viewWillAppear" method using transitionCoordinator. That its view.
edited: strong text add logic, when your transition from search table, and navigation is hide at this moment. (searchController.hidesNavigationBarDuringPresentation = true)
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
if navigationItem.searchController?.isActive == false {
navigationBack.frame = self.navigationController?.navigationBar.frame ?? CGRect.zero
}
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height += self.navigationItem.searchController?.searchBar.frame.height ?? 0
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
Set background color of navigation controller view to the same color of navigation bar seems to work around the issue for me.
navigationController?.view.backgroundColor = navigationController?.navigationBar.barTintColor
What helped was to add this in viewController with largeTitles enabled:
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
navigationBack.frame = (self.navigationController?.navigationBar.frame)!
navigationBack.frame.size.height = 44
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height = (self.navigationItem.searchController?.searchBar.frame.height)! + (self.navigationController?.navigationBar.frame.height)!
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
and this in viewController with largeTitles disabled:
override func viewWillAppear(_ animated: Bool) {
let navigationBack = UIView()
navigationBack.frame = self.navigationController?.navigationBar.frame ?? CGRect.zero
navigationBack.backgroundColor = navigationController?.navigationBar.barTintColor
let containerView = transitionCoordinator?.containerView
transitionCoordinator?.animateAlongsideTransition(in: containerView, animation: { (context) in
containerView?.addSubview(navigationBack)
navigationBack.frame.size.height = 44
}, completion: { (context) in
navigationBack.removeFromSuperview()
})
super.viewWillAppear(animated)
}
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)
I am using UIBlurEffect in a UIViewController which is presented with a .crossDissolve transition style. The UIViewController has a collectionView added to its view so both have a clear background.
HomeViewController.swift
func showLiveDealsCategory(sender:UITextField){
let catSelection = LiveDealCategorySelection()
//let navContr = UINavigationController(rootViewController: catSelection)
catSelection.modalPresentationStyle = .custom
catSelection.modalTransitionStyle = .crossDissolve
self.present(catSelection, animated: true, completion: nil)
}
LiveDealCategorySelection.swift
func setupBackgroundView(){
if !UIAccessibilityIsReduceTransparencyEnabled() {
self.view.backgroundColor = .clear
let blurEffect = UIBlurEffect(style: UIBlurEffectStyle.dark)
let blurEffectView = UIVisualEffectView(effect: blurEffect)
blurEffectView.frame = (self.view?.bounds)!
blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
self.view?.addSubview(blurEffectView)
} else {
self.collectionView?.backgroundColor = UIColor.black
}
}
This is the result where you can see the mapView behind LiveDealCategorySelection:
The problem is when I embed the view controller inside a UINavigationController because I am not able to set its background color to .clear:
func showLiveDealsCategory(sender:UITextField){
let catSelection = LiveDealCategorySelection()
let navContr = UINavigationController(rootViewController: catSelection)
catSelection.modalPresentationStyle = .custom
catSelection.modalTransitionStyle = .crossDissolve
self.present(navContr, animated: true, completion: nil)
}
In LiveDealCategorySelection I tried:
override func viewWillAppear(_ animated: Bool) {
if self.navigationController != nil {
self.navigationController!.view.backgroundColor = .clear
self.navigationController!.view.tintColor = .clear
}
}
and I also tried to set the background color to .clear when I instantiate the navigation controller but I get a black background. Any idea?
You just forgot to move the presentation style to the UINavigationController
navContr.modalPresentationStyle = .custom
With navigation controller you cannot achieve that you want. Only Modal presentation with OverCurrentContext, supports transition from one view controller to another with clear or semi transparent background.
If you want blur background for this view controller then present this view controller using modal presentation with 'custom (or over current context)'
else try this:
func showLiveDealsCategory(sender:UITextField){
let catSelection = LiveDealCategorySelection()
let navContr = UINavigationController(rootViewController: catSelection)
navContr.modalPresentationStyle = .custom // << Mark this update
catSelection.modalPresentationStyle = .custom
catSelection.modalTransitionStyle = .crossDissolve
self.present(navContr, animated: true, completion: nil)
}
I want to present my view controller with a custom transition so the fromViewController will be darkened as if presenting a AlertViewController.
I have created my customTransition manager:
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
let container = transitionContext.containerView()
let fromView = transitionContext.viewForKey(UITransitionContextFromViewKey)!
let toView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let fadeRect = CGRectMake(0, 0, fromView.frame.size.width, fromView.frame.size.height)
let fadeView = UIView(frame: fadeRect)
if (self.presenting == true){
fadeView.backgroundColor = UIColor.blackColor()
fadeView.alpha = 0
fromView.addSubview(fadeView)
container.insertSubview(fadeView, aboveSubview: fromView)
} else {
// adding subviews to container
}
let duration = self.transitionDuration(transitionContext)
UIView.animateWithDuration(duration, animations: { () -> Void in
if (self.presenting == true){
fadeView.alpha = 0.5
} else {
}
}) { (Bool) -> Void in
if self.presenting{
container.addSubview(toView)
}
transitionContext.completeTransition(true)
}
}
}
I assign my transitioning manager to the toViewController as it can be seen below:
var purchaseSpecialItemsViewController = self.storyboard?.instantiateViewControllerWithIdentifier("specialItemPurchaseVC") as! BRSpecialItemPurchaseViewController
purchaseSpecialItemsViewController.transitioningDelegate = self.fadedTransitionManager
self.presentViewController(purchaseSpecialItemsViewController, animated: true, completion: nil)
The View Controller I am about to display is in storyboard with a fixed size. It has a clear background and I added a tableview there that I want to be in the center of the screen.
The TableView is displayed without the problem, but I can't see the fromViewController at all. It is just black even though I set the alpha of the black background to 0 which is later animated to 0.5.
I am presenting the view controller modally, is that causing the problem? Should I use instead a push and pop operation?
As described here: http://mathewsanders.com/custom-menu-transitions-in-swift/
I needed to add the modal presentation style like this:
purchaseSpecialItemsViewController.modalPresentationStyle = UIModalPresentationStyle.OverFullScreen
and instead getting ViewControllersViews, use
let screens: (from: UIViewController, to: UIViewController) = (transitionContext.viewControllerForKey(UITransitionContextFromViewControllerKey)!, transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)!)
to access the ViewControllers.