How to create Sidebar menu in IOS swift? [duplicate] - ios

This question already has answers here:
Slide Sidebar Menu IOS 8 Swift [closed]
(7 answers)
Closed 6 years ago.
I am searching for some suggestions how to create a sidebar menu in swift not in storyboard or xib only.

I believe you can start form UISplitViewController which was
drastically updated in iOS8. Watch sessions View Controller
Advancements in
iOS8 and
Building Adaptive Apps with
UIKit for
details. They provide code example from second
session
(but not form first one :/). At this point it feels natural for me to
make this kind of UI based on split view controller in iOS8.
checkout: Slide Sidebar Menu IOS 8 Swift
You can also checkout this Tutorial:
http://www.appcoda.com/sidebar-menu-swift/
It's a great Tutorial with an example.

work for me...
I created a new view controller as "MenuViewController", and two customs segues class.
The first class to open and the second to close the menu.
TO OPEN THE MENU:
import Foundation
import UIKit
class SegueFromLeft: UIStoryboardSegue {
override func perform() {
let src = self.source as UIViewController
let dst = self.destination as UIViewController
src.view.superview?.insertSubview(dst.view, aboveSubview: src.view)
dst.view.transform = CGAffineTransform(translationX: -src.view.frame.size.width, y: 0)
UIView.animate(withDuration: 0.25,
delay: 0.0,
options: UIViewAnimationOptions.curveEaseInOut,
animations: {
dst.view.transform = CGAffineTransform(translationX: 0, y: 0)
},
completion: { finished in
src.present(dst, animated: false, completion: nil)
}
)
}
}
TO CLOSE THE MENU:
import Foundation
import UIKit
class SegueFromRight: UIStoryboardSegue {
override func perform() {
let src = self.source as UIViewController
let dst = self.destination as UIViewController
src.view.superview?.insertSubview(dst.view, belowSubview: src.view)
src.view.transform = CGAffineTransform(translationX: 0, y: 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: { finished in
src.dismiss(animated: false, completion: nil)
}
)
}
}
This tutorial is for a menu from left to right, but if you want in the opposite direction, change :
-src.view.frame.size.width
to:
src.view.frame.size.width
I hope it helped you...

Related

Common iOS Memory Leak Issue Due to Custom Segues. How to fix?

I have seen variations of the following code all over StackOverflow:
import UIKit
class segueFromLeft: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let src = self.source.view as UIView!
let dst = self.destination.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
// Specify the initial position of the destination view.
dst?.frame = CGRect(x: screenWidth, y: 0, width: screenWidth,
height: screenHeight)
// Access the app's key window and insert the destination view
above the current (source) one.
let window = UIApplication.shared.keyWindow
window?.insertSubview(dst!, aboveSubview: src!)
// Animate the transition.
UIView.animate(withDuration: 0.5, animations: { () -> Void in
src?.frame = (src?.frame.offsetBy(dx: -screenWidth, dy: 0))!
dst?.frame = (dst?.frame.offsetBy(dx: -screenWidth, dy: 0))!
}) { (Finished) -> Void in
self.source.present(self.destination, animated: false, completion: nil) {
}
}
}
}
At first, the code operates as a nice way of transitioning from one view to another. But with continued use, most of the problems that have been listed on this website as a result from it relate to memory. Every time the segue is used, the destination view is initialized and the source view remains in memory. With continued use, the memory use continues to grow and grow.
A simple dismissal of the source view did not function for me, the screen just went black.
My question is, how can we fix this problem?

How to prevent from stacking views in a custom segue?

I coded a custom segue emulating a "Push Left". I takes screenshots of the 2 views, animates them from right to left, then present the destination view and remove the overlaying screenshots. I'm afraid that this ultimately results in stacking views on top of one another, which should be avoided in this case. I can't figure out how to properly dismiss the underlaying view once the animation is completed. I tried to use navigationController?.pushViewController instead of present but my attempts were not successful. How could I solve this issue ?
My custom segue :
class SeguePushLeft: UIStoryboardSegue
{
override func perform()
{
self.source.view.isUserInteractionEnabled = false
self.destination.view.isUserInteractionEnabled = false
let slideViewOut = UIImageView(image: source.view.capture()!)
let slideViewIn = UIImageView(image: destination.view.capture()!)
let screenWidth = source.view.frame.size.width
self.source.view.addSubview(slideViewIn)
self.source.view.addSubview(slideViewOut)
slideViewIn.transform = CGAffineTransform(translationX: screenWidth, y: 0)
UIView.animate(withDuration: 0.4,
delay: 0.0,
usingSpringWithDamping: 1,
initialSpringVelocity: 0,
options: UIViewAnimationOptions.curveEaseOut,
animations: {
slideViewIn.transform = CGAffineTransform.identity
slideViewOut.transform = CGAffineTransform(translationX: -screenWidth, y: 0)
}, completion: { finished in
DispatchQueue.main.async{
(self.source as UIViewController).present((self.destination as UIViewController), animated: false, completion: {
self.source.view.isUserInteractionEnabled = true
self.destination.view.isUserInteractionEnabled = true
slideViewIn.removeFromSuperview()
slideViewOut.removeFromSuperview()
})
}
})
}
}
[Please forgive my previous too-hasty answer. I'm entering another rather than editing the first, because of all the comments that now blot the first.]
The problem is simple: you're doing this wrong. What you wish to do is a present transition, but with a custom animation. That's perfectly viable, and you can certainly do it with a custom segue, but the way you're doing it is not how you do that. You do it with a custom transition animation. There is a fixed way of implementing that, and it isn't how you're going about things. You need to read up on how to write a custom transition animation for a presentation transition and you'll be all set.

insertSubview:belowSubview still places view above swift 3.0

I'm trying to make a segue in my app such that the current view slides away to reveal a view below it.
This is my code:
let window = UIApplication.shared.delegate?.window!
window?.insertSubview(destinationView, belowSubview: sourceView)
destinationView.center = CGPoint(x: sourceView.center.x, y: sourceView.center.y)
UIView.animate(withDuration: 0.6, animations: {
sourceView.center = CGPoint(x: sourceView.center.x, y: 0 - sourceView.center.y)
}, completion: { (value: Bool) in
self.source.navigationController?.pushViewController(self.destination, animated: false)
})
However, the view that I'm segueing to simply appears over the top of the view I'm currently on. Any ideas on what I'm doing wrong?
Thanks!
Well, I solved it. Here was my solution in case anyone else has this problem:
window?.insertSubview(destinationView, belowSubview: sourceView)
window?.insertSubview(sourceView, aboveSubview: destinationView)
I just added that second line of code and it worked.

Unable to remove from superview (swift2)

I am unable to remove from superview with the following code?
Why is this?I have tried everything but seems that it is not working at all.I am adding more details so I don't get this very annoying alert that tells me that my post is mostly code....
let controller = storyboard!.instantiateViewControllerWithIdentifier("OrderViewController")
controller.view.frame = CGRectMake(self.view.frame.size.width/2 - 100, self.view.frame.size.height/2 - 100, 200, 200)
if sender.selected {
sender.selected = false
controller.view.transform = CGAffineTransformIdentity
[UIView .animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
controller.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
}, completion: { finished in
controller.willMoveToParentViewController(nil)
controller.view .removeFromSuperview()
controller.removeFromParentViewController()
})]
print("close")
}
else {
sender.selected = true
addChildViewController(controller)
view.addSubview(controller.view)
controller.didMoveToParentViewController(self)
controller.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
[UIView .animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
controller.view.transform = CGAffineTransformIdentity
}, completion: nil)]
print("present")
}
There's a weird mix of objective-c syntax (the square brackets around your UIView animation block) and swift. I'm surprised if this is even compiling without errors!
You're almost there, the main issue is that each time this block of code is called you're instantiating a new instance of an 'OrderViewController'.
You only want to create this if it doesn't exist (when you want to show it). When you're ready to hide it you want to grab a reference to the existing controller and do the animations etc to hide it.
To do that you'll need to keep a reference to it outside of the local scope of that code block.
Here's an example showing how you might do that:
import UIKit
class ViewController: UIViewController {
// keep a reference to the OrderViewController after it's been created
var controller:UIViewController?
#IBAction func buttonTapped(sender: AnyObject) {
// instead of using button selected state, just check to see if
// self.controller is nil or not
if let existingController = self.controller {
// controller already exists, lets hide it
existingController.view.transform = CGAffineTransformIdentity
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
existingController.view.transform = CGAffineTransformMakeScale(0.01, 0.01);
}, completion: { _ in
existingController.willMoveToParentViewController(nil)
existingController.view.removeFromSuperview()
existingController.removeFromParentViewController()
// make this nil, so that next time the button is
// tapped we'll go though the process of creating it again
self.controller = nil
})
print("close")
}
else {
// controller doesn't exist, lets instanstiate and show it
let newController = storyboard!.instantiateViewControllerWithIdentifier("OrderViewController")
newController.view.frame = CGRectMake(self.view.frame.size.width/2 - 100, self.view.frame.size.height/2 - 100, 200, 200)
addChildViewController(newController)
view.addSubview(newController.view)
newController.didMoveToParentViewController(self)
newController.view.transform = CGAffineTransformMakeScale(0.01, 0.01)
UIView.animateWithDuration(0.2, delay: 0.0, options: UIViewAnimationOptions.CurveEaseOut, animations: {
newController.view.transform = CGAffineTransformIdentity
}, completion: { _ in
// keep a reference to this controller you've just created,
// so that next time the button is tapped you can close it
self.controller = newController
})
print("present")
}
}
}
If you're working on a really simple app (just a couple of screens) then this method is okay.
But.... if you're planning on something more complex you might want to investigate using either a custom UISegue, or a mix of UIPresentationController and UIViewControllerAnimatedTransitioning so that all of this animation logic doesn't get locked into your view controller code.

Custom Segue not working if NOT set as initial window

Following is the code excerpts for the custom segue animation in Swift, which works fine if I make the source viewController as Initial Window in storyboard, only.
class CustomSegue: UIStoryboardSegue
{
override func perform()
{
var fromView = self.sourceViewController.view as UIView!
var toView = self.destinationViewController.view as UIView!
let offScreenHorizontalStart = CGAffineTransformMakeRotation(CGFloat(M_PI / 2))
let offScreenHorizontalEnd = CGAffineTransformMakeRotation(CGFloat(-M_PI / 2))
fromView.layer.anchorPoint = CGPoint(x:0, y:0)
fromView.layer.position = CGPoint(x:0, y:0)
UIApplication.sharedApplication().keyWindow?.insertSubview(toView, belowSubview: fromView)
UIView.animateWithDuration(0.7, delay: 0.0, usingSpringWithDamping: 1.6, initialSpringVelocity: 0.0, options: nil, animations:
{
fromView.transform = offScreenHorizontalEnd
}, completion: {
finished in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated: false, completion: nil)
})
}
}
By this segue animation, the source view expects to disappear with an upside rotation from x/y = 0/0.
Thanks.
As far as you are using explicitly the keyWindow property of the UIApplication object here:
UIApplication.sharedApplication().keyWindow?.insertSubview(toView, belowSubview: fromView)
I think it is no surprise that it won't work for other windows.
You could use the windows property of UIApplication instead. It holds all UIWindow objects currently open in the app in an ordered NSArray (the last object in the array is the topmost window).
Or you pick the current window from the fromView.window property?

Resources