Swift: Slide out Menu - ios

I am developing an iOS application in Swift. I want to display a left slide out menu so I followed Ray Wenderlich's tutorial ( http://www.raywenderlich.com/78568/create-slide-out-navigation-panel-swift#comments )
This tutorial is for a left and right slide out menu, but I only want the left one. So I used the part about the left one; however, it only displays a black menu instead of the left one I want. How can I make the menu display correctly?

This is the code of the site modified to show only the left part, see it , it removes all the part where the right panel is open.
import UIKit
import QuartzCore
enum SlideOutState {
case Collapsed
case Expanded
}
class ContainerViewController: UIViewController, CenterViewControllerDelegate, UIGestureRecognizerDelegate {
var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!
var mainPanelExpandedOffset: CGFloat = 518
var currentState: SlideOutState = .Collapsed {
didSet {
let shouldShowShadow = currentState != .Collapsed
showShadowForCenterViewController(shouldShowShadow)
}
}
var leftViewController: SidePanelViewController?
var rightViewController: SidePanelViewController?
let centerPanelExpandedOffset: CGFloat = 60
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
centerViewController.delegate = self
// wrap the centerViewController in a navigation controller, so we can push views to it
// and display bar button items in the navigation bar
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "handlePanGesture:")
centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)
}
// MARK: CenterViewController delegate methods
func toggleLeftPanel() {
let notAlreadyExpanded = (currentState != .Expanded)
if notAlreadyExpanded {
addLeftPanelViewController()
}
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
func collapseSidePanels() {
switch (currentState) {
case .Expanded:
toggleLeftPanel()
default:
break
}
}
func addLeftPanelViewController() {
if (leftViewController == nil) {
leftViewController = UIStoryboard.leftViewController()
leftViewController!.animals = Animal.allCats()
addChildSidePanelController(leftViewController!)
}
}
func addChildSidePanelController(sidePanelController: SidePanelViewController) {
sidePanelController.delegate = centerViewController
view.insertSubview(sidePanelController.view, atIndex: 0)
addChildViewController(sidePanelController)
sidePanelController.didMoveToParentViewController(self)
}
func animateLeftPanel(#shouldExpand: Bool) {
if (shouldExpand) {
currentState = .Expanded
animateCenterPanelXPosition(targetPosition: CGRectGetWidth(leftViewController!.view.frame) - 100)
} else {
animateCenterPanelXPosition(targetPosition: 0) { finished in
self.currentState = .Collapsed
self.leftViewController?.view.removeFromSuperview()
self.leftViewController = nil;
}
}
}
func animateCenterPanelXPosition(#targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func showShadowForCenterViewController(shouldShowShadow: Bool) {
if (shouldShowShadow) {
centerNavigationController.view.layer.shadowOpacity = 0.8
} else {
centerNavigationController.view.layer.shadowOpacity = 0.0
}
}
// MARK: Gesture recognizer
func handlePanGesture(recognizer: UIPanGestureRecognizer) {
let gestureIsDraggingFromLeftToRight = (recognizer.velocityInView(view).x > 0)
switch(recognizer.state) {
case .Changed:
if (gestureIsDraggingFromLeftToRight && currentState == .Expanded){
recognizer.view!.center.x = recognizer.view!.center.x + recognizer.translationInView(view).x
recognizer.setTranslation(CGPointZero, inView: view)
currentState = .Collapsed
}
case .Ended:
if (gestureIsDraggingFromLeftToRight) {
// animate the side panel open or closed based on whether the view has moved more or less than halfway
let hasMovedGreaterThanHalfway = recognizer.view!.center.x > view.bounds.size.width
animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
}
default:
break
}
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func rightViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("RightViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
}
You can try too this Creating a Sidebar Menu Using SWRevealViewController in Swift I thought it's a good place too.
I hope this help you

Related

UIPresentationController change size for a moment when another view is on top

I am trying to make a pop over form the bottom of screen using UIPresentationController, so I followed raywenderlich guide here : https://www.raywenderlich.com/139277/uipresentationcontroller-tutorial-getting-started. I did the exact same thing, I only change the size and y position of the frame. The pop up consist of buttons that open the share sheet , but for some reason when I open the sheet then click "save to files", the "shave to files" view shows up and when I hit cancel my pop over goes full screen for a moment then changes to my custom size.
I tried to debug the app and found out that containerViewWillLayoutSubviews() doesn't get called untill the "save to file" view is dismissed. Anyone have an idea on how to solve this. Thank you
this is my code :
main :
final class MainViewController: UIViewController {
// MARK: - Properties
lazy var slideInTransitioningDelegate = SlideInPresentationManager()
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func showPopup(_ sender: Any) {
let controller = storyboard.instantiateViewController(withIdentifier: NSStringFromClass(MyPopUpController.self))
as! MyPopUpController
slideInTransitioningDelegate.direction = .bottom
slideInTransitioningDelegate.disableCompactHeight = true
controller.transitioningDelegate = slideInTransitioningDelegate
controller.modalPresentationStyle = .custom
}
mypopucontroller
final class MyPopUpController: UIViewController {
#IBAction func share(_ sender: Any) {
let activityController = UIActivityViewController(activityItems: ["message"], applicationActivities: nil)
present(activityController, animated: true)
}
// MARK: - View Life Cycle
override func viewDidLoad() {
super.viewDidLoad()
}
slide in presentation controller :
final class SlideInPresentationController: UIPresentationController {
// MARK: - Properties
fileprivate var dimmingView: UIView!
private var direction: PresentationDirection
override var frameOfPresentedViewInContainerView: CGRect {
var frame: CGRect = .zero
frame.size = size(forChildContentContainer: presentedViewController, withParentContainerSize: containerView!.bounds.size)
switch direction {
case .right:
frame.origin.x = containerView!.frame.width*(1.0/3.0)
case .bottom:
frame.origin.y = containerView!.frame.height*0.5
default:
frame.origin = .zero
}
return frame
}
// MARK: - Initializers
init(presentedViewController: UIViewController, presenting presentingViewController: UIViewController?, direction: PresentationDirection) {
self.direction = direction
super.init(presentedViewController: presentedViewController, presenting: presentingViewController)
setupDimmingView()
}
override func presentationTransitionWillBegin() {
containerView?.insertSubview(dimmingView, at: 0)
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "V:|[dimmingView]|", options: [], metrics: nil, views: ["dimmingView": dimmingView]))
NSLayoutConstraint.activate(NSLayoutConstraint.constraints(withVisualFormat: "H:|[dimmingView]|", options: [], metrics: nil, views: ["dimmingView": dimmingView]))
guard let coordinator = presentedViewController.transitionCoordinator else {
dimmingView.alpha = 1.0
return
}
coordinator.animate(alongsideTransition: { _ in
self.dimmingView.alpha = 1.0
})
}
override func dismissalTransitionWillBegin() {
guard let coordinator = presentedViewController.transitionCoordinator else {
dimmingView.alpha = 0.0
return
}
coordinator.animate(alongsideTransition: { _ in
self.dimmingView.alpha = 0.0
})
}
override func containerViewWillLayoutSubviews() {
presentedView?.frame = frameOfPresentedViewInContainerView
}
override func size(forChildContentContainer container: UIContentContainer, withParentContainerSize parentSize: CGSize) -> CGSize {
switch direction {
case .left, .right:
return CGSize(width: parentSize.width*(2.0/3.0), height: parentSize.height)
case .bottom, .top:
return CGSize(width: parentSize.width, height: parentSize.height*0.67)
}
}
}
// MARK: - Private
private extension SlideInPresentationController {
func setupDimmingView() {
dimmingView = UIView()
dimmingView.translatesAutoresizingMaskIntoConstraints = false
dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
dimmingView.alpha = 0.0
let recognizer = UITapGestureRecognizer(target: self, action: #selector(handleTap(recognizer:)))
dimmingView.addGestureRecognizer(recognizer)
}
dynamic func handleTap(recognizer: UITapGestureRecognizer) {
presentingViewController.dismiss(animated: true)
}
}
slidein manager :
final class SlideInPresentationManager: NSObject {
// MARK: - Properties
var direction = PresentationDirection.left
var disableCompactHeight = false
}
// MARK: - UIViewControllerTransitioningDelegate
extension SlideInPresentationManager: UIViewControllerTransitioningDelegate {
func presentationController(forPresented presented: UIViewController, presenting: UIViewController?, source: UIViewController) -> UIPresentationController? {
let presentationController = SlideInPresentationController(presentedViewController: presented, presenting: presenting, direction: direction)
presentationController.delegate = self
return presentationController
}
func animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideInPresentationAnimator(direction: direction, isPresentation: true)
}
func animationController(forDismissed dismissed: UIViewController) -> UIViewControllerAnimatedTransitioning? {
return SlideInPresentationAnimator(direction: direction, isPresentation: false)
}
}
// MARK: - UIAdaptivePresentationControllerDelegate
extension SlideInPresentationManager: UIAdaptivePresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController, traitCollection: UITraitCollection) -> UIModalPresentationStyle {
if traitCollection.verticalSizeClass == .compact && disableCompactHeight {
return .overFullScreen
} else {
return .none
}
}
func presentationController(_ controller: UIPresentationController, viewControllerForAdaptivePresentationStyle style: UIModalPresentationStyle) -> UIViewController? {
guard case(.overFullScreen) = style else { return nil }
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "RotateViewController")
}
}
slidein animator:
final class SlideInPresentationAnimator: NSObject {
// MARK: - Properties
let direction: PresentationDirection
let isPresentation: Bool
// MARK: - Initializers
init(direction: PresentationDirection, isPresentation: Bool) {
self.direction = direction
self.isPresentation = isPresentation
super.init()
}
}
// MARK: - UIViewControllerAnimatedTransitioning
extension SlideInPresentationAnimator: UIViewControllerAnimatedTransitioning {
func transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval {
return 0.3
}
func animateTransition(using transitionContext: UIViewControllerContextTransitioning) {
let key = isPresentation ? UITransitionContextViewControllerKey.to : UITransitionContextViewControllerKey.from
let controller = transitionContext.viewController(forKey: key)!
if isPresentation {
transitionContext.containerView.addSubview(controller.view)
}
let presentedFrame = transitionContext.finalFrame(for: controller)
var dismissedFrame = presentedFrame
switch direction {
case .left:
dismissedFrame.origin.x = -presentedFrame.width
case .right:
dismissedFrame.origin.x = transitionContext.containerView.frame.size.width
case .top:
dismissedFrame.origin.y = -presentedFrame.height
case .bottom:
dismissedFrame.origin.y = transitionContext.containerView.frame.size.height
}
let initialFrame = isPresentation ? dismissedFrame : presentedFrame
let finalFrame = isPresentation ? presentedFrame : dismissedFrame
let animationDuration = transitionDuration(using: transitionContext)
controller.view.frame = initialFrame
UIView.animate(withDuration: animationDuration, animations: {
controller.view.frame = finalFrame
}) { finished in
transitionContext.completeTransition(finished)
}
}
}
You can try to subclass UIPresentationController and override var presentedView: UIView? and enforce presentedView's frame.
override var presentedView: UIView? {
super.presentedView?.frame = frameOfPresentedViewInContainerView
return super.presentedView
}
See example: "Custom View Controller Presentation" from Kyle Bashour https://kylebashour.com/posts/custom-view-controller-presentation-tips

How can I block any interaction on MapView? [duplicate]

This question already has answers here:
How to disable user interaction on MKMapView?
(4 answers)
Closed 6 years ago.
I'm designing a Slide Navigation Panel due to that tutorial.
Everything works fine. Now I want to go further and work on some UX issues.
This is how my app look like now. But still I can manipulate MapView. My goal is to do two things:
1) Prevent all interaction with MapView, such as scaling and moving
2) Close navigation panel, while tapping on viewController with map.
The main file for controlling this two controlller is containerViewController. Here is the source code for it:
import UIKit
import QuartzCore
enum SlideOutState {
case BothCollapsed
case LeftPanelExpanded
}
class ContainerViewController: UIViewController {
var centerNavigationController: UINavigationController!
var centerViewController: ViewController!
var currentState: SlideOutState = .BothCollapsed {
didSet {
let shouldShowShadow = currentState != .BothCollapsed
showShadowForCenterViewController(shouldShowShadow)
}
}
var leftViewController: SidePanelViewController?
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
centerViewController.delegate = self
// wrap the centerViewController in a navigation controller, so we can push views to it
// and display bar button items in the navigation bar
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
}
}
let centerPanelExpandedOffset: CGFloat = 60
extension ContainerViewController: CenterViewControllerDelegate {
func toggleLeftPanel() {
let notAlreadyExpanded = (currentState != .LeftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController()
}
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
func addLeftPanelViewController() {
if (leftViewController == nil) {
leftViewController = UIStoryboard.leftViewController()
leftViewController!.categories = Category.allCats()
addChildSidePanelController(leftViewController!)
}
}
func addChildSidePanelController(sidePanelController: SidePanelViewController) {
view.insertSubview(sidePanelController.view, atIndex: 0)
addChildViewController(sidePanelController)
sidePanelController.didMoveToParentViewController(self)
}
func animateLeftPanel(shouldExpand shouldExpand: Bool) {
if (shouldExpand) {
currentState = .LeftPanelExpanded
animateCenterPanelXPosition(targetPosition: CGRectGetWidth(centerNavigationController.view.frame) - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0) { finished in
self.currentState = .BothCollapsed
self.leftViewController!.view.removeFromSuperview()
self.leftViewController = nil;
}
}
}
func animateCenterPanelXPosition(targetPosition targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func showShadowForCenterViewController(shouldShowShadow: Bool) {
if (shouldShowShadow) {
centerNavigationController.view.layer.shadowOpacity = 0.8
} else {
centerNavigationController.view.layer.shadowOpacity = 0.0
}
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func centerViewController() -> ViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? ViewController
}
}
My idea is to create variable that stands for active side panel and write a function, that will solve my issues:
I see something like that:
func blockCenterView() {
let AlreadyExpanded = (currentState = .LeftPanelExpanded)
if AlreadyExpanded {
//no idea what to write here
}
}
Am I right, or there exist other variants for solving that problem?
Try to set the following properties to false
mapView.zoomEnabled = false
mapView.scrollEnabled = false
mapView.userInteraction = false
Also check the Interface Builder for the user Interactions:
Add this code whereever you want to stop interaction with mapView
mapView.userInteractionEnabled = false
To enable it back,
mapView.userInteractionEnabled = true
Note : mapView is an instance of MKMapView
PS
SWRevealViewController
Source : Github
Step By Step Tutorial : More Flexible Slide View
This is what I was able to create using this awesome framework
App Link : AirMate on AppStore

Protocols problems in Swift

I'm implementing the RayWenderlich Slide out Navigation Panel: http://www.raywenderlich.com/78568/create-slide-out-navigation-panel-swift
And I want to open a new view ont Imageview tap like if I was clicking on the menu:
Link 2: NosOffres
Link 1: (index) Accueil (here is the Imageview)
Problem:
If I go in Link 2 from the Navigation/Menu, I can re-open the Navigation/Menu by slide or icon menu tap
If I go in Link 2 from the Image View, I can re-open the Navigation/Menu by slide but not with by icon menu tap
I tried many solutions and asked here for help but no way to have a response, now I tried an another thing, but I have a protocol problem:
I can't redeclare a protocol (in CenterViewController.swift), so I don't know how to do:
CenterViewController.swift:
#objc protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
protocol CenterViewControllerDelegate {
func itemSelected(item: AccueilItem)
}
class CenterViewController: UIViewController {
var delegate: CenterViewControllerDelegate?
var menus: Array<Menu>!
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 38.0/255.0, green: 51.0/255.0, blue: 85.0/255.0, alpha: 1.0)
self.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Gotham", size: 13)!, NSForegroundColorAttributeName : UIColor.whiteColor()]
self.title = "ACCUEIL"
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func menuTapped(sender: AnyObject) {
delegate?.toggleLeftPanel?()
}
#IBAction func nosOffresTapped(sender: AnyObject) {
print("lol")
delegate?.itemSelected(AccueilItem(rawValue: "NosOffres")!)
}
}
enum AccueilItem: String {
case Accueil
case NosOffres
case DemandeGratuite
case ContactezNous
case Actualites
case MentionsLegales
func viewController() -> UIViewController {
switch (self) {
case Accueil: return UIStoryboard.centerViewController()!
case NosOffres: return UIStoryboard.nosOffresViewController()!
case DemandeGratuite: return {
let vc = UIViewController();
vc.view.backgroundColor = UIColor.orangeColor();
return vc
}()
case ContactezNous: return UIStoryboard.nosOffresViewController()!
case Actualites: return UIStoryboard.nosOffresViewController()!
case MentionsLegales: return UIStoryboard.nosOffresViewController()!
}
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
class func nosOffresViewController() -> NosOffresViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("NosOffresViewController") as? NosOffresViewController
}
}
NosOffresViewController.swift:
#objc
protocol NosOffresViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class NosOffresViewController: UIViewController {
var delegate: NosOffresViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.navigationBar.barTintColor = UIColor(red: 38.0/255.0, green: 51.0/255.0, blue: 85.0/255.0, alpha: 1.0)
self.navigationController?.navigationBar.titleTextAttributes = [NSFontAttributeName: UIFont(name: "Gotham", size: 13)!, NSForegroundColorAttributeName : UIColor.whiteColor()]
self.title = "NOS OFFRES"
// Do any additional setup after loading the view, typically from a nib.
}
}
ContainerViewController.swift:
enum SlideOutState{
case BothCollapsed
case LeftPanelExpanded
}
class ContainerViewController: UIViewController, CenterViewControllerDelegate, SidePanelViewControllerDelegate, UIGestureRecognizerDelegate {
var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!
var currentState: SlideOutState = .BothCollapsed
var leftViewController: SidePanelViewController?
let centerPanelExpandedOffset: CGFloat = 60
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
centerViewController.delegate = self
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "handlePanGesture:")
centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)
}
}
// MARK: CenterViewController delegate
extension ContainerViewController {
func itemSelected(item: MenuItem) {
let vc = item.viewController()
//create a new button
let button: UIButton = UIButton(type: UIButtonType.Custom)
//set image for button
button.setImage(UIImage(named:"IconeMenu"), forState: UIControlState.Normal)
//add function for button
button.addTarget(self, action: "toggleLeftPanel", forControlEvents: UIControlEvents.TouchUpInside)
//set frame
button.frame = CGRectMake(0, 0,30, 30)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
vc.navigationItem.leftBarButtonItem = barButton
self.centerNavigationController.viewControllers = [vc]
self.collapseSidePannels()
}
func nosOffresSelected(item: AccueilItem) {
let vc = item.viewController()
//create a new button
let button: UIButton = UIButton(type: UIButtonType.Custom)
//set image for button
button.setImage(UIImage(named:"IconeMenu"), forState: UIControlState.Normal)
//add function for button
button.addTarget(self, action: "toggleLeftPanel", forControlEvents: UIControlEvents.TouchUpInside)
//set frame
button.frame = CGRectMake(0, 0,30, 30)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
vc.navigationItem.leftBarButtonItem = barButton
self.centerNavigationController.viewControllers = [vc]
self.collapseSidePannels()
}
}
extension ContainerViewController {
func toggleLeftPanel() {
let notAlreadyExpanded = (currentState != .LeftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController()
}
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
func collapseSidePannels() {
switch (currentState) {
case .LeftPanelExpanded:
toggleLeftPanel()
default:
break
}
}
func addLeftPanelViewController() {
if (leftViewController == nil) {
leftViewController = UIStoryboard.leftViewController()
leftViewController!.menus = Menu.allMenu()
addChildSidePanelController(leftViewController!)
}
}
func addChildSidePanelController(sidePanelController: SidePanelViewController) {
sidePanelController.delegate = self
view.insertSubview(sidePanelController.view, atIndex: 0)
addChildViewController(sidePanelController)
sidePanelController.didMoveToParentViewController(self)
}
func animateLeftPanel(shouldExpand shouldExpand: Bool) {
if (shouldExpand) {
currentState = .LeftPanelExpanded
animateCenterPanelXPosition(targetPosition: CGRectGetWidth(centerNavigationController.view.frame) - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0) { finished in
self.currentState = .BothCollapsed
self.leftViewController!.view.removeFromSuperview()
self.leftViewController = nil;
}
}
}
func animateCenterPanelXPosition(targetPosition targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
}
extension ContainerViewController {
// MARK: Gesture recognizer
func handlePanGesture(recognizer: UIPanGestureRecognizer) {
let gestureIsDraggingFromLeftToRight = (recognizer.velocityInView(view).x > 0)
switch(recognizer.state) {
case .Began:
if (currentState == .BothCollapsed) {
if (gestureIsDraggingFromLeftToRight) {
addLeftPanelViewController()
}
}
case .Changed:
if (((recognizer.view!.center.x + recognizer.translationInView(view).x) > view.center.x || gestureIsDraggingFromLeftToRight) && (recognizer.view!.center.x >= view.center.x && recognizer.velocityInView(view).x > 0 || recognizer.view!.center.x > view.center.x && recognizer.velocityInView(view).x < 0) && recognizer.view!.center.x + recognizer.translationInView(view).x > view.center.x) {
recognizer.view!.center.x = recognizer.view!.center.x + recognizer.translationInView(view).x
recognizer.setTranslation(CGPointZero, inView: view)
}
case .Ended:
if (leftViewController != nil) {
// animate the side panel open or closed based on whether the view has moved more or less than halfway
let hasMovedGreaterThanHalfway = recognizer.view!.center.x > view.bounds.size.width
animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
}
default:
break
}
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
class func nosOffresViewController() -> NosOffresViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("NosOffresViewController") as? NosOffresViewController
}
}
SidePanelViewController.swift:
protocol SidePanelViewControllerDelegate {
func itemSelected(item: MenuItem)
}
class SidePanelViewController: UIViewController {
#IBOutlet weak var tableView: UITableView!
var delegate: SidePanelViewControllerDelegate?
var menus: Array<Menu>!
struct TableView {
struct CellIdentifiers {
static let MenuCell = "MenuCell"
}
}
override func viewDidLoad() {
super.viewDidLoad()
let tblView = UIView(frame: CGRectZero)
tableView.tableFooterView = tblView
tableView.tableFooterView!.hidden = true
tableView.backgroundColor = UIColor(red: 71.0/255.0, green: 88.0/255.0, blue: 130.0/255.0, alpha: 1.0)
tableView.reloadData()
}
}
extension SidePanelViewController: UITableViewDataSource {
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 6
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier(TableView.CellIdentifiers.MenuCell, forIndexPath: indexPath) as! MenuCell
cell.configureForMenu(menus[indexPath.row])
return cell
}
}
// Mark: Table View Delegate
extension SidePanelViewController: UITableViewDelegate {
func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
_ = menus[indexPath.row]
delegate?.itemSelected(MenuItem(rawValue: indexPath.row)!)
}
}
enum MenuItem: Int {
case Accueil
case NosOffres
case DemandeGratuite
case ContactezNous
case Actualites
case MentionsLegales
func viewController() -> UIViewController {
switch (self) {
case Accueil: return UIStoryboard.centerViewController()!
case NosOffres: return UIStoryboard.nosOffresViewController()!
case DemandeGratuite: return {
let vc = UIViewController();
vc.view.backgroundColor = UIColor.orangeColor();
return vc
}()
case ContactezNous: return UIStoryboard.nosOffresViewController()!
case Actualites: return UIStoryboard.nosOffresViewController()!
case MentionsLegales: return UIStoryboard.nosOffresViewController()!
}
}
}
class MenuCell: UITableViewCell {
#IBOutlet weak var label: UILabel!
func configureForMenu(menu: Menu) {
label.text = menu.title
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
class func nosOffresViewController() -> NosOffresViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("NosOffresViewController") as? NosOffresViewController
}
}
I really don't know how to do here I tried to do the same thing of SidePanelViewController to CenterViewController, but I don't know if they're an another solution, help me please.
Your problem resides in when you use the menu to show a new UIViewController you are manipulating the navigationItem in your ContainerViewController in this way:
func itemSelected(item: MenuItem) {
let vc = item.viewController()
//create a new button
let button: UIButton = UIButton(type: UIButtonType.Custom)
//set image for button
button.setImage(UIImage(named:"IconeMenu"), forState: UIControlState.Normal)
//add function for button
button.addTarget(self, action: "toggleLeftPanel", forControlEvents: UIControlEvents.TouchUpInside)
//set frame
button.frame = CGRectMake(0, 0,30, 30)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
vc.navigationItem.leftBarButtonItem = barButton
self.centerNavigationController.viewControllers = [vc]
self.collapseSidePannels()
}
And for the above code the UINavigationController remains the same when you open the NosOffresViewController.
One way to solve your problem is add a new method in your CenterViewControllerDelegate and call it before push the new UIViewController and pass the reference to it:
#objc protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
optional func pushViewControllerInStack(viewController: UIViewController)
}
Like the following way:
#IBAction func nosOffresTapped(sender: AnyObject) {
self.navigationController?.pushViewController(UIStoryboard.nosOffresViewController()!, animated: false)
self.delegate?.pushViewControllerInStack!(UIStoryboard.nosOffresViewController()!)
}
And then in your ContainerViewController implements the method like in the following way:
func pushViewControllerInStack(viewController: UIViewController) {
let vc = viewController
//create a new button
let button: UIButton = UIButton(type: UIButtonType.Custom)
//set image for button
button.setImage(UIImage(named:"IconeMenu"), forState: UIControlState.Normal)
//add function for button
button.addTarget(self, action: "toggleLeftPanel", forControlEvents: UIControlEvents.TouchUpInside)
//set frame
button.frame = CGRectMake(0, 0,30, 30)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
vc.navigationItem.leftBarButtonItem = barButton
self.centerNavigationController.viewControllers = [vc]
self.collapseSidePannels()
}
And this should work to present any new UIViewController from the CenterViewController.
I hope this help you.

UITabBarController height decreases everytime it's loaded

When I first load my application and log in. Everything is fine.
However when I log out, then log back in. The height of my view has been decreased. Here's a screenshot of the bug:
I havn't been able to find the cause of this. Making this quite a difficult question to ask help with as I can't specify the precise section of code causing the issue. But I'll try.
The problematic setup is like so:
I have a containerViewController, with 2 childViewControllers, a menu and a UITabBarController. The UITabBarController has 2 UIViewControllers.
To better explain it, here's a visual representation.
_______________________
App Start ->
NavigationController(rootViewController LandingPageVC)
LandingPageVC -> push -> SignInVC(this is where I login from)
SignInVC -> push -> ContainerViewController(this has my UITabBarController and my menu)
ContainerViewController (sets up my menuTabBarController and menu)
menuTabBarController (this tabBarController is used to switch out my content from the menu)
SidePanelViewController (this is my menu)
ContainerViewController -> push(signing out) -> LandingPageVC
_______________________
Here's how I push the containerViewController when a successful login is called.
let mainTableViewController = ContainerViewController()
mainTableViewController.navigationItem.setHidesBackButton(true, animated: false)
navigationController!.pushViewController(mainTableViewController, animated: true)
menuEnabled = true
here's the function called from the containerViewController I use to log out.
func signOut() {
// Set up the landing page as the main viewcontroller again.
let mainTableViewController = LandingPageVC()
mainTableViewController.navigationItem.setHidesBackButton(true, animated: false)
mainTableViewController.skipView = false
self.navigationController!.pushViewController(mainTableViewController, animated: true)
// Disable menu access
menuEnabled = false
}
by printing the height of ContainerViewController and menuTabBarController, I found that it is the UITabBarController's height that's decreasing and not the ContainerViewController.
Here's the code that has to do with the UITabBarController
import UIKit
import QuartzCore
let menuTabBarController = UITabBarController()
var menuButton = UIBarButtonItem()
var menuEnabled = false
class ContainerViewController: UIViewController, CenterViewControllerDelegate, SidePanelViewControllerDelegate, UIGestureRecognizerDelegate {
func needsSignOut(sender: SidePanelViewController) {
// toggling left panel
self.toggleLeftPanel()
// signing out
self.signOut()
}
var centerViewController: UITabBarController!
var leftViewController: SidePanelViewController?
let centerPanelExpandedOffset: CGFloat = 60
override func viewDidLoad() {
super.viewDidLoad()
menuTabBarController.tabBar.hidden = true
menuButton = UIBarButtonItem(title: "", style: UIBarButtonItemStyle.Plain, target: self, action: "toggleLeftPanel")
if let font = UIFont(name: "FontAwesome", size: 20) {
menuButton.setTitleTextAttributes([NSFontAttributeName: font], forState: UIControlState.Normal)
}
self.navigationItem.leftBarButtonItem = menuButton
//let tabBarController = UITabBarController()
let suggestionsVC = SuggestionsViewController()
let testVC = detaiLSuggestion_VC()
let controllers = [suggestionsVC,testVC]
menuTabBarController.setViewControllers(controllers, animated: false)
centerViewController = menuTabBarController
view.addSubview(menuTabBarController.view)
addChildViewController(menuTabBarController)
//centerNavigationController.didMoveToParentViewController(self)
}
// MARK: CenterViewController delegate methods
func toggleLeftPanel() {
let notAlreadyExpanded = (currentState != .LeftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController()
}
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
func collapseSidePanels() {
switch (currentState) {
case .LeftPanelExpanded:
toggleLeftPanel()
default:
break
}
}
func addLeftPanelViewController() {
if (leftViewController == nil) {
leftViewController = SidePanelViewController()
leftViewController!.delegate = self
addChildSidePanelController(leftViewController!)
}
}
func addChildSidePanelController(sidePanelController: SidePanelViewController) {
view.insertSubview(sidePanelController.view, atIndex: 0)
addChildViewController(sidePanelController)
sidePanelController.didMoveToParentViewController(self)
}
func animateLeftPanel(#shouldExpand: Bool) {
if (shouldExpand) {
currentState = .LeftPanelExpanded
animateCenterPanelXPosition(targetPosition: CGRectGetWidth(centerViewController.view.frame) - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0) { finished in
self.currentState = .BothCollapsed
self.leftViewController!.view.removeFromSuperview()
self.leftViewController = nil;
}
}
}
func animateCenterPanelXPosition(#targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
self.centerViewController.view.frame.origin.x = targetPosition
}, completion: completion)
}
Any help deducing where this is coming from or how I can go about fixing it would be greatly appreciated! And again I apologize for the dumb of code. I'll update it further if I am able to rule out parts of it.
rdelmar found a solution for me in chat.
The problem was fixed by specifying the menuTabBarController.view.frame like so:
menuTabBarController.view.frame = self.view.frame

NSInvalidArgumentException reason: 'Storyboard doesn't contain a view controller with identifier 'CenterViewController''

Error: Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Storyboard (<UIStoryboard: 0x7ff949770c30>) doesn't contain a view controller with identifier 'CenterViewController''
I dont' know what's wrong with my Main Storyboard.I was building slide out navigation panel of my own.
CenterViewController.swift
import UIKit
#objc
protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class CenterViewController: UIViewController, SideMenuPanelViewControllerDelegate {
//#IBOutlet weak private var testimageView: UIImageView!
#IBOutlet weak private var testLabel:UILabel!
var delegate: CenterViewControllerDelegate?
// MARK: Button actions
#IBAction func MenuTapped(sender: AnyObject) {
delegate?.toggleLeftPanel?()
}
func localSelected(local: LocalMenus) {
//imageView.image = animal.image
testLabel.text = local.title
delegate?.collapseSidePanels?()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}
ContainerViewController.swift
import UIKit
import QuartzCore
enum SideOutState {
case BothCollapsed
case LeftPanelExpanded
case RightPanelExpanded
}
class ContainerViewController: UIViewController, CenterViewControllerDelegate, UIGestureRecognizerDelegate{
var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!
var currentState: SideOutState = .BothCollapsed {
didSet {
let shouldShowShadow = currentState != .BothCollapsed
showShadowForCenterViewController(shouldShowShadow)
}
}
var leftMenuController: SideMenuPanelViewController?
let centerPanelExpandedOffset: CGFloat = 60
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
centerViewController.delegate = self
// wrap the centerViewController in a navigation controller, so we can push views to it
// and display bar button items in the navigation bar
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
let panGestureRecognizer = UIPanGestureRecognizer(target: self, action: "handlePanGesture:")
centerNavigationController.view.addGestureRecognizer(panGestureRecognizer)
}
// MARK: CenterViewController delegate methods
func toggleLeftPanel() {
let notAlreadyExpanded = (currentState != .LeftPanelExpanded)
if notAlreadyExpanded {
addLeftPanelViewController()
}
animateLeftPanel(shouldExpand: notAlreadyExpanded)
}
func collapseSidePanels() {
switch (currentState) {
case .LeftPanelExpanded:
toggleLeftPanel()
default:
break
}
}
func addLeftPanelViewController() {
if (leftMenuController == nil) {
leftMenuController = UIStoryboard.leftMenuController()
leftMenuController!.local = LocalMenus.allLocal()
addChildSidePanelController(leftMenuController!)
}
}
func addChildSidePanelController(sidePanelController:SideMenuPanelViewController) {
sidePanelController.delegate = centerViewController
view.insertSubview(sidePanelController.view, atIndex: 0)
addChildViewController(sidePanelController)
sidePanelController.didMoveToParentViewController(self)
}
func animateLeftPanel(#shouldExpand: Bool) {
if (shouldExpand) {
currentState = .LeftPanelExpanded
animateCenterPanelXPosition(targetPosition: CGRectGetWidth(centerNavigationController.view.frame) - centerPanelExpandedOffset)
} else {
animateCenterPanelXPosition(targetPosition: 0) { finished in
self.currentState = .BothCollapsed
self.leftMenuController!.view.removeFromSuperview()
self.leftMenuController = nil;
}
}
}
func animateCenterPanelXPosition(#targetPosition: CGFloat, completion: ((Bool) -> Void)! = nil) {
UIView.animateWithDuration(0.5, delay: 0, usingSpringWithDamping: 0.8, initialSpringVelocity: 0, options: .CurveEaseInOut, animations: {
self.centerNavigationController.view.frame.origin.x = targetPosition
}, completion: completion)
}
func showShadowForCenterViewController(shouldShowShadow: Bool) {
if (shouldShowShadow) {
centerNavigationController.view.layer.shadowOpacity = 0.8
} else {
centerNavigationController.view.layer.shadowOpacity = 0.0
}
}
// MARK: Gesture recognizer
func handlePanGesture(recognizer: UIPanGestureRecognizer) {
// we can determine whether the user is revealing the left or right
// panel by looking at the velocity of the gesture
let gestureIsDraggingFromLeftToRight = (recognizer.velocityInView(view).x > 0)
switch(recognizer.state) {
case .Began:
if (currentState == .BothCollapsed) {
// If the user starts panning, and neither panel is visible
// then show the correct panel based on the pan direction
if (gestureIsDraggingFromLeftToRight) {
addLeftPanelViewController()
}
showShadowForCenterViewController(true)
}
case .Changed:
// If the user is already panning, translate the center view controller's
// view by the amount that the user has panned
recognizer.view!.center.x = recognizer.view!.center.x + recognizer.translationInView(view).x
recognizer.setTranslation(CGPointZero, inView: view)
case .Ended:
// When the pan ends, check whether the left or right view controller is visible
if (leftMenuController != nil) {
// animate the side panel open or closed based on whether the view has moved more or less than halfway
let hasMovedGreaterThanHalfway = recognizer.view!.center.x > view.bounds.size.width
animateLeftPanel(shouldExpand: hasMovedGreaterThanHalfway)
}
default:
break
}
}
}
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard {
return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
}
class func leftMenuController() -> SideMenuPanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("SideMenuPanelViewController") as? SideMenuPanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
}
Very simple.
In your main storyboard, you have no ViewController with a Storyboard ID "CenterViewController"
Look here:
https://stackoverflow.com/a/11604827/3324388
Short answer from Xcode 8.0 onwards,
Go to the Main.storyboard select the target ViewController, press Command+option+3 to display the attributes
Fill the StoryBoard ID input field
Check the Use Storyboard ID checkbox
I know this could be found on the duplicated referred answer but you need to read, try and error etc.

Resources