I am using Swift 3 and xcode 8 to build a slide out menu for an iOS app (I don't want to use any open source library) so I had built it , I am facing two problems ,
1.If the center view has a navigation bar , then the side menu view is appearing beneath the navigation bar , I want it to start from the screen bounds.
2.The slide menu view also shows the carrier , time on top of the menu view , I want the behavior similar to google plus iOS app , where the menu loads on top of the home view.
Please find below the code I use to open the slide menu , I am not sure if the above issues are happening because I had added the menu view as a subview , if there is a better way to do it , kindly suggest.
Opening the menu view from the home view controller
let menuVC : MenuViewController = self.storyboard!.instantiateViewController(withIdentifier: "MenuViewController") as! MenuViewController
menuVC.delegate = self
self.view.addSubview(menuVC.view)
self.addChildViewController(menuVC)
menuVC.didMove(toParentViewController: self)
menuVC.view.layoutIfNeeded()
menuVC.view.frame=CGRect(x: 0 - UIScreen.main.bounds.size.width, y: 0, width: self.view.frame.width, height: self.view.frame.height);
UIView.animate(withDuration: 0.3, animations: { () -> Void in
menuVC.view.frame = self.view.frame
sender.isEnabled = true
}, completion:nil)
If you really want to view to be on top of the status bar and the navcontroller you can add it to the window:
UIApplication.shared.keyWindow?.addSubview(menuVC.view)
Note that the view is no longer a subview of the ViewController so you also need to manually dismiss it in deinit otherwise it will stay on screen even when the view controller goes away:
menuVC.view.removeFromSuperview()
You can add left menu as a subview over application window and slide it using the animation.
Hide/show the status bar if you don't want to show the carrier , time on top of the menu view
class ViewController: UIViewController {
#IBOutlet weak var leading: NSLayoutConstraint!
#IBOutlet weak var sideview: UIView!
var showmenu = false
override func viewDidLoad() {
super.viewDidLoad()
leading.constant = -160
// sideview.layer.shadowOpacity = 5
// sideview.layer.shadowRadius = 5
}
#IBAction func btnmenu(_ sender: UIBarButtonItem) {
if (showmenu)
{
leading.constant = -160
}
else
{
leading.constant = 0
UIView.animate(withDuration: 0.5, animations:{self.view.layoutIfNeeded() })
}
showmenu = !showmenu
}
#IBAction func btntblview(_ sender: UIButton) {
let hk = storyboard?.instantiateViewController(withIdentifier: "TableViewController")as! TableViewController
self.navigationController?.pushViewController(hk, animated: true)
}
Related
I have a UIViewController A which presents modally another UIViewController B which takes up only the bottom half the screen height.
Back when I was still using Xcode 10, when the modal ViewController B is presented, a dark overlay will cover ViewController A and I will also set the view.alpha of ViewController A to 0.5 using these methods:
func presentBottomSheet() {
let viewController = BottomSheetModalVC()
viewController.modalPresentationStyle = .overCurrentContext
DispatchQueue.main.async { [weak self] in
self?.dimParent()
self?.parentViewController?.present(viewController, animated: true, completion: nil)
}
}
func dimParent() {
UIView.transition(with: parentVC.view, duration: 0.6, options: [.curveEaseOut], animations: {
parentVC.view.alpha = 0.5
})
}
in order to shift the user's focus to the modal view. However, when I tried to compile this with Xcode 11, the black overlay is done and I am left with a parent view who only becomes half visible when the modal view slides up from the bottom. Was there a change in this overlay behaviour?
My screen looks something similar to this. But after Xcode 11, the black overlay is no longer there leaving me with a completely transparent overlay.
Create a UIView in ViewControllerB. In your case its BottomSheetModalVC
var backgroundView: UIView = {
let view = UIView()
view.backgroundColor = UIColor.black.withAlphaComponent(0.7)
return view
}()
Add the backgroundView as a subView to the presentingViewController's view
override func viewWillLayoutSubviews() {
presentingViewController?.view.addSubview(backgroundView)
backgroundView.frame = presentingViewController?.view.bounds ?? .zero
}
Remove this backgroundView in viewWillDisappear
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
UIView.animate(withDuration: 0.3) {
self.backgroundView.removeFromSuperview()
}
}
Found my own answer to this silly question.
So it turns out that the UINavigationController presenting ViewController A & B had its background color set to white so when ViewController A has its alpha set to 0.5, it just looks like it was disappearing but not dimming.
I have set my NavigationController background color to black now and everything is back to normal.
I have a MasterViewController and I am adding a ChildViewController inside a MasterViewController. Inside MasterViewController I have made top button bar , and took a view as a Container view. In this container view I want to move my ChildViewControllers. And This will happen on button click in MasterViewController
This is how I am adding the ChildViewController
func addViewControllerAsChildViewController(childVC : UIViewController) {
oldVC = newVC
childVC.view.frame = viewContainer.bounds
childVC.view.autoresizingMask = [.flexibleWidth, .flexibleHeight]
addChildViewController(childVC)
childVC.didMove(toParentViewController: self)
// Where ViewContainer is dedicated area inside the MasterViewController
viewContainer.addSubview(childVC.view)
}
And these are my button clicks
#IBAction func onClickBtnNewList(_ sender: UIButton) {
if !(newVC is NewListVC) {
initNewListVC()
addViewControllerAsChildViewController(childVC: targetViewController)
}
UIView.animate(withDuration: 4000, animations: {
self.viewIndicators.frame = sender.frame.offsetBy(dx:0,dy:2)
})
}
#IBAction func onClickBtnSavedList(_ sender: UIButton) {
if !(newVC is SavedListVC) {
initSavedListVC()
addViewControllerAsChildViewController(childVC: targetViewController)
}
UIView.animate(withDuration:400, animations: {
self.viewIndicators.frame = sender.frame.offsetBy(dx:0,dy:2)
})
}
My ChildViewControllers gets inflated correctly. But Problem is on button Click there is a another view, which I am animating on click and moving it below the button. You can see I have used the following code in button click. The animation start and ended moving my related view to its starting position. and its not moving to my expected area see code below
UIView.animate(withDuration: 4000, animations: {
self.viewIndicators.frame = sender.frame.offsetBy(dx:0,dy:2)
})
but this is not working. But If I comment out following lines that are attaching ChildViewController , The animation do move smoothly and View moves to the target area
/*
if !(newVC is NewListVC) {
initNewListVC()
hideOrShowViewController(targetViewController: newVC!)
}
*/
Now do you have any idea why it is happening ?? what could be problem?
I am using Xcode 9 and swift 4 Please help
I created an additional UIWindow that will get presented on top of the main window. The user presses a button and it transitions over the main window. The user can minimize the additional window and it will sit above the tabBar like it the picture below. They can enlarge it to cover the main window or dismiss it and it gets destroyed. It works fine.
I also created a custom action sheet that launches from the bottom of the screen. If the additional window completely covers the main window and the action sheet is launched, it will launch perfectly inside the additional window. If the additional window isn't on the screen it launches perfectly inside the main window.
The problem is if the additional window is minimized on screen and I want to launch the action sheet from the main window, the action sheet launches inside the additional window instead, it cannot distinguish between the two. I opened up the 3D visualizer and it showed that the main window was off and the additional window was on.
How can I distinguish between both windows when displaying the custom action sheet?
Btw if both windows are present and the action sheet is launched from the main window I hide the additional window. I also looked at other answers and they said to use UIApplication.shared.keyWindow.addSubview which I'm already doing.
CustomActionSheet Class:
var collectionView: UICollectionView!
var deltaY: CGFloat!
let height: CGFloat = 200 // 4 cells x 50 pts each
func displayActionSheet(){
if let window = UIApplication.shared.keyWindow {
// collectionView initialized...
window.addSubview(collectionView)
deltaY = window.frame.height - height
collectionView.frame = CGRect(x: 0, y: window.frame.height, width: window.frame.width, height: height)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: [.curveEaseOut], animations: {
self.collectionView.frame = CGRect(x: 0, y: deltaY, width: self.collectionView.frame.width, height: self.collectionView.frame.height)
}
}
}
MainView Class:
#IBAction func mainWindowActionSheetButton(_ sender: UIButton) {
let customActionSheet = CustomActionSheet()
customActionSheet.displayActionSheet()
}
AdditionalWindow Class:
let myVC = MyController()
var nav: UINavigationController?
var window: UIWindow!
var maximized = true
override init() {
super.init()
window = UIWindow()
window.backgroundColor = .clear
window.windowLevel = UIWindowLevelStatusBar
nav = UINavigationController(rootViewController: myVC)
window.rootViewController = nav
window?.isHidden = false
window?.makeKeyAndVisible()
}
func maximizeOrMinimizeWindow() {
if maximized {
// show this full screen
} else {
// show this window minimized like in the picture
}
}
AnotherController Class that has the button that also launches the action sheet:
#IBAction func additionalWindowActionSheetButton(_ sender: UIButton) {
let customActionSheet = CustomActionSheet()
customActionSheet.displayActionSheet()
}
The summarize the advice in comments.
The problem is that the action sheet is always shown from the key window but the additional window remains key window even when minimized.
The obvious solution is to make the main window the key window when the additional one is being minimized. See UIWindow.makeKey() or UIWindow.makeKeyAndVisible().
Since UIApplication.shared.windows are ordered by window level (the back one first), you can always reach the main window using UIApplication.shared.windows.first.
Therefore
UIApplication.shared.windows.first?.makeKey()
will make the main window the key window and the minimized window will stop being the key window.
Here's the code breakdown to #Sulthan 's accepted answer. Read the comments in the code for an explanation.
let myVC = MyController()
var nav: UINavigationController?
var window: UIWindow!
var maximized = true
override init() {
super.init()
window = UIWindow()
window.backgroundColor = .clear
window.windowLevel = UIWindowLevelStatusBar
nav = UINavigationController(rootViewController: myVC)
window.rootViewController = nav!
window?.isHidden = false
// window?.makeKeyAndVisible() // don't call this because it doesn't need to be the keyWindow as of yet. The window?.isHidden property above this makes the window visible
}
func maximizeOrMinimizeWindow() {
if maximized {
window.first?.makeKey // when the additional window is maximized make it the keyWindow
} else {
UIApplication.shared.windows.first?.makeKey() // when the additional window is minimized set the main window back as the key window
}
}
It should also be stated then when the additional window is removed from the superview or destroyed make sure to set the main window back as the keyWindow using UIApplication.shared.windows.first?.makeKey()
I have a storyboard segue, and while showing the new view, I want an UIView to always stay on top, so the segue does not affect it. Tried animating insertSubview, but it does not have the push from bottom animation.
Here is some code I quickly whipped up showing two view controllers, the one you are going to and one from. I put the animation in a function called buttonPressed, but it should go whatever function that calls the transition. Rest of the code is pretty self explanatory.
For this to work both view controllers will need a view with same name (or different names but keep track of which is which), I use IBOutlet staticView. And then in interface builder make sure they have the same constraints or frame so that when you set on vc's static view to another it sticks to same spot.
class ViewControllerFrom: UIViewController {
#IBOutlet weak var staticView: UIView!
#IBAction func buttonPressed(_ sender: Any) {
let toVC = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "toVC") as! ViewControllerTo
//Add nextVC's view to ours as subview
self.view.addSubview(toVC.view)
//Set its starting height to be below current view.
toVC.view.frame = CGRect(x: 0, y: view.frame.height, width: view.frame.width, height: view.frame.height)
//This is important to make sure staticView stays in front of view animating in
view.bringSubview(toFront: staticView)
if toVC.staticView != nil {
toVC.staticView.isHidden = true
}
//I use animation duration 0.4, close to default animations by iOS.
UIView.animate(withDuration: 0.4, animations: {
toVC.view.frame.origin.y = 0
}, completion: { (success) in
if success {
//Now that view is in place, set static view from old vc to new vc and reshow it. Then do the actual presentation unanimated.
toVC.staticView.isHidden = false
toVC.staticView = self.staticView
self.present(toVC, animated: false, completion: nil)
}
})
}
}
class ViewControllerTo : UIViewController {
#IBOutlet weak var staticView: UIView!
}
EDIT
I think my problem is that I add the views as subviews in the same view, thats why I can't remove it ?
Im trying to learn swiping between views using XIB.
My storyboard contains 3 views
-Login
-Create Account
-View with scrollview that scrolls between a tableview and a blank view. This view has an embedded navigation controller (Editor -> Embed In -> Navigation Controller)
I Don't want the navigation controller to be shown in my blank page.
I have created the tableView Controller and the blank UIControllerView by adding them as "addChildViewController", See code below
import UIKit
class MasterViewForScroll: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
let Inbox : FriendlistTableBarView = FriendlistTableBarView(nibName: "FriendlistTableBarView", bundle: nil)
let Camera : CameraViewController = CameraViewController(nibName: "CameraViewController", bundle: nil)
func creatingSubViews() {
self.addChildViewController(Inbox)
self.scrollView.addSubview(Inbox.view)
Inbox.didMoveToParentViewController(self)
Inbox.navigationController?.navigationBar.hidden = false
var CameraView = Camera.view.frame
CameraView.origin.x = self.view.frame.width
Camera.view.frame = CameraView
self.addChildViewController(Camera)
self.scrollView.addSubview(Camera.view)
Camera.didMoveToParentViewController(self)
self.scrollView.contentSize = CGSizeMake(self.view.frame.width * 2, self.view.frame.height)
}
override func viewDidLoad() {
super.viewDidLoad()
creatingSubViews()
}
So my question is: How do I hide the navigation controller in the "Camera" view.
Thank you
In your CameraViewController class, add the following code:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBarHidden = true
}
Next, in your FriendlistTableBarView class (I guess it is a UIViewController subclass), add the following code:
override func viewWillAppear() {
super.viewWillAppear()
self.navigationController?.navigationBarHidden = false
}
So, when you swipe to the right - navigation bar will hide, and when you swipe to the left - it will appear again.