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.
Related
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.
I have a animation that I want to be infinite
var rotateRock: Bool = true
func rotateRock() {
UIView.animate(withDuration: 2.7, delay: 2.7, options: [UIViewAnimationOptions.repeat, UIViewAnimationOptions.curveLinear], animations:
{
self.rockImg.transform.ty += 155
if self.rotateRock {
self.rotateRock = false
self.rockImg.transform = CGAffineTransform(rotationAngle: (180.0 * CGFloat(M_PI)) / 180.0)
} else {
self.rotateRock = true
self.rockImg.transform = CGAffineTransform(rotationAngle: 0)
}
},completion: nil)
}
Then I call rotateRock() inside viewDidLoad() this works, the animation never stops which is what I want.
If I now enter another VC that is presented modally then exit it so I end up in my first VC the animation is still running which I want.
But if I change VC by clicking on another tab in my tab bar and then go back the animation has stopped. Is there any way to check if the animation has stopped so that I can restart it?
You could override viewDidAppear() and call rotateRock there. That should work.
Edit:
What Matt says below is true, viewDidAppear would work but maybe it would be better viewWillAppear to make sure you don't see it before starting.
I am using this code for periscope-style comments in my iOS app (where the comment bubbles slide up from the bottom): https://github.com/yoavlt/PeriscommentView
And this is the code that actually animates the comments in and out:
public func addCell(cell: PeriscommentCell) {
cell.frame = CGRect(origin: CGPoint(x: 0, y: self.frame.height), size: cell.frame.size)
visibleCells.append(cell)
self.addSubview(cell)
UIView.animateWithDuration(self.config.appearDuration, delay: 0, options: UIViewAnimationOptions.CurveEaseOut, animations: { () -> Void in
let dy = cell.frame.height + self.config.layout.cellSpace
for c in self.visibleCells {
let origin = c.transform
let transform = CGAffineTransformMakeTranslation(0, -dy)
c.transform = CGAffineTransformConcat(origin, transform)
}
}, completion: nil)
UIView.animateWithDuration(self.config.disappearDuration, delay: self.config.stayDuration, options: UIViewAnimationOptions.CurveEaseIn, animations: { () -> Void in
cell.alpha = 0.0
}) { (Bool) -> Void in
self.visibleCells.removeLast()
cell.removeFromSuperview()
}
}
The problem with the above code is that sometimes when a new comment is added, it shows up overlapping the previous comment. The expected behavior is that the previous comment slides up and the new comment takes its place. I noticed that this mainly happens when you add a new comment after the previous comment starts to fade out, but still has not disappeared.
I tried putting a breakpoint in the self.visibleCells.removeLast(), and it does seem like this gets called only when the last comments completed disappears, so I would expect this to work correctly (because the for loop moves up all the visible cells, and even when a comment is fading out, it is still visible).
Any help with this would be appreciated.
Thanks!
I just got a clone of that repository, run on my device and your problem is trying to rewrite the functionality. Do not do that. Instead, just add this line:
#IBAction func addNewCell(sender: UIButton) {
self.periscommentView.addCell(UIImage(named: "twitterProfile")!, name: "Your_Name_Here", comment: "Your_Comment_Here")
}
That's all! I just checked it and it works perfectly!
Do not try to change alpha or moving up. The library does all of these stuffs! Good luck! ;)
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...
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?