I am attempting to segue by having the source view controller go from the top to bottom. I can get the transition to go from top to bottom, but as the transition is happening, there is a black rectangle that goes from top to bottom. And once that's finished, the destination view controller pops up. Anybody know how to solve this problem? I am using this tutorial: http://www.appcoda.com/custom-segue-animations/ and changing bits and piece of it to do what I want it to do.
import UIKit
class FirstCustomSegue: UIStoryboardSegue {
override func perform() {
var firstVCView = self.sourceViewController.view as UIView!
var secondVCView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(0.0, 0, screenWidth, screenHeight)
// Access the app's key window and insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(2, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, screenHeight)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController,
animated: false,
completion: nil)
}
}
}
Update your code like this, Is it working correctly now ?
secondVCView.frame = CGRectMake(0.0, -screenHeight, screenWidth, screenHeight)
For those who are looking for the full solution in Swift 4:
override func perform() {
let firstVCView = self.source.view
let secondVCView = self.destination.view
// 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.
secondVCView!.frame = CGRect(x: 0, y: -screenHeight, 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(secondVCView!, aboveSubview: firstVCView!)
// Animate the transition.
UIView.animate(withDuration: 1, animations: { () -> Void in
firstVCView?.frame = (firstVCView?.frame.offsetBy(dx: 0, dy: screenHeight))!
secondVCView?.frame = (secondVCView?.frame.offsetBy(dx: 0, dy: screenHeight))!
}) { (finished) -> Void in
self.source.present(self.destination, animated: false, completion: nil)
}
}
Related
I'm trying to learn basic iOS Swift3 and using the storyboard method. I was working through the code in the tutorial on this website.
Having created the storyboard links for FirstCustomSegue, I code as follows; I have added comments for all 6 errors:
class FirstCustomSegue: UIStoryboardSegue {
override func perform() {
// Value of type 'FirstCustomSegue' has no member 'sourceGameViewController'
var firstVCView = self.sourceGameViewController.view as UIView!
var secondVCView = self.destinationGameViewController.view as UIView!
// Cannot call value of non-function type 'UIScreen'
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
secondVCView.frame = CGRectMake(0.0, screenHeight, screenWidth, screenHeight)
// Cannot call value of non-function type 'UIApplication'
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// Value of type 'FirstCustomSegue' has no member 'sourceGameViewController' (This is in the (Finished) line, of course)
UIView.animateWithDuration(0.4, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, -screenHeight)
secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, -screenHeight)
}) { (Finished) -> Void inself.sourceGameViewController.presentGameViewController(self.destinationGameViewController as UIViewController,
animated: false,
completion: nil)
}
}
}
Looks to me as though there's a missing connection somewhere, but I can't find it. Perhaps some of it is a change in iOS10? Some searching has suggested that this may be the problem with the UIScreen.mainScreen() bits, but I can't find anything that resolves the problems.
I rewrote your code into Swift 3.0 style. But do you use Xcode 8? If you use Xcode 8, the compiler may help you to fix syntax errors.
class FirstCustomSegue: UIStoryboardSegue {
override func perform() {
let firstVCView: UIView = self.source.view
let secondVCView: UIView = self.destination.view
let screenWidth = UIScreen.main.bounds.size.width
let screenHeight = UIScreen.main.bounds.size.height
secondVCView.frame = CGRect(x: 0.0, y: screenHeight, width: screenWidth, height: screenHeight)
let window = UIApplication.shared.keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
UIView.animate(withDuration: 0.4, animations: {
firstVCView.frame = firstVCView.frame.offsetBy(dx: 0.0, dy: -screenHeight)
secondVCView.frame = secondVCView.frame.offsetBy(dx: 0.0, dy: -screenHeight)
}, completion: { [unowned self] finished in
self.source.present(self.destination, animated: false, completion: nil)
})
}
}
You can also convert your code into latest Swift syntax by yourself.
Edit > Convert > To Current Swift Syntax...
https://i.stack.imgur.com/niayG.png
You need to use
UIApplication.shared
and
UIScreen.main
in Swift 3. (Can't comment yet.)
The UITabBar disappears after I execute a custom segue.
Two views are embedded in a UITabBar Controller
First View Before Segue:
Second View After Segue:
First View After Segue:
Main Storyboard:
Custom Segue Code:
import UIKit
class CustomPushSegue: UIStoryboardSegue {
override func perform(){
// Assign the source and destination views to local variables.
let firstVCView = self.sourceViewController.view as UIView!
let secondVCView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(screenWidth, 0.0, screenWidth, screenHeight)
//Insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(0.35, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, -screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, -screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as UIViewController, animated: false, completion: nil)
}
}
}
Custom Unwind Segue:
import UIKit
class CustomPushUnwindSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let secondVCView = self.sourceViewController.view as UIView!
let firstVCView = self.destinationViewController.view as UIView!
//Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(0.0, 0.0, screenWidth, screenHeight)
//Insert the destination view above the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(firstVCView, aboveSubview: secondVCView)
// Animate the transition.
UIView.animateWithDuration(0.35, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, screenWidth, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
}
}
}
I've written a custom segue to get a fade effect, which i'm trying to achieve by inserting the destination view controller below the source view controller and animating the alpha of the source view controller to zero.
However, adding the destination view controller below the source seems to cancel the animation and the segue performs as if it is a regular present with animation disabled.
import UIKit
class FadeSegue: UIStoryboardSegue {
override func perform() {
// Get the view of the source
let sourceViewControllerView = self.sourceViewController.view
// Get the view of the destination
let destinationViewControllerView = self.destinationViewController.view
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Make the destination view the size of the screen
destinationViewControllerView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
if let window = UIApplication.sharedApplication().keyWindow {
// Insert destination below the source
// Without this line the animation works but the transition is not smooth as it jumps from white to the new view controller
window.insertSubview(destinationViewControllerView, belowSubview: sourceViewControllerView)
// Animate the fade, remove the destination view on completion and present the full view controller
UIView.animateWithDuration(0.4, animations: {
sourceViewControllerView.alpha = 0.0
}, completion: { (finished) in
destinationViewControllerView.removeFromSuperview()
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: nil)
})
}
}
}
Hello I think that what you need is this
override func perform() {
// Get the view of the source
let sourceViewControllerView = self.sourceViewController.view
// Get the view of the destination
let destinationViewControllerView = self.destinationViewController.view
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Make the destination view the size of the screen
destinationViewControllerView.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
// Insert destination below the source
// Without this line the animation works but the transition is not smooth as it jumps from white to the new view controller
destinationViewControllerView.alpha = 0;
sourceViewControllerView.addSubview(destinationViewControllerView);
// Animate the fade, remove the destination view on completion and present the full view controller
UIView.animateWithDuration(10, animations: {
destinationViewControllerView.alpha = 1;
}, completion: { (finished) in
destinationViewControllerView.removeFromSuperview()
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: nil)
})
}
}
I Hope this help you
The slightly modified Swift 4.1 compatible solution based on the answer from #reinier-melian
/// Performs a simple fade between source and destination view controller.
class Fade: UIStoryboardSegue {
override func perform() {
guard let destinationView = self.destination.view else {
// Fallback to no fading
self.source.present(self.destination, animated: false, completion: nil)
return
}
destinationView.alpha = 0
self.source.view?.addSubview(destinationView)
UIView.animate(withDuration: CATransaction.animationDuration(), animations: {
destinationView.alpha = 1
}, completion: { _ in
self.source.present(self.destination, animated: false, completion: nil)
})
}
}
This is what I ended up doing for my purposes. It puts a placeholder view with basic ui elements over the top once all the on screen things have animated out and fades in the placeholder, then presents the destination view controller and removes the placeholder. The destination view controller then animates in all its elements, essentially making the transition invisible and looks like one single animation:
import UIKit
class FadeSegue: UIStoryboardSegue {
var placeholderView: UIViewController?
override func perform() {
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
if let placeholder = placeholderView {
placeholder.view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
placeholder.view.alpha = 0
sourceViewController.view.addSubview(placeholder.view)
UIView.animateWithDuration(0.4, animations: {
placeholder.view.alpha = 1
}, completion: { (finished) in
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: {
placeholder.view.removeFromSuperview()
})
})
} else {
self.destinationViewController.view.alpha = 0.0
self.sourceViewController.presentViewController(self.destinationViewController, animated: false, completion: {
UIView.animateWithDuration(0.4, animations: {
self.destinationViewController.view.alpha = 1.0
})
})
}
}
}
and the unwind:
import UIKit
class FadeSegueUnwind: UIStoryboardSegue {
var placeholderView: UIViewController?
override func perform() {
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
if let placeholder = placeholderView {
placeholder.view.frame = CGRect(x: 0, y: 0, width: screenWidth, height: screenHeight)
placeholder.view.alpha = 0
sourceViewController.view.addSubview(placeholder.view)
UIView.animateWithDuration(0.4, animations: {
placeholder.view.alpha = 1
}, completion: { (finished) in
self.sourceViewController.dismissViewControllerAnimated(false, completion: nil)
})
} else {
self.destinationViewController.view.alpha = 0.0
self.sourceViewController.dismissViewControllerAnimated(false, completion: {
UIView.animateWithDuration(0.4, animations: {
self.destinationViewController.view.alpha = 0.0
})
})
}
}
}
I have the following swift code and what i am trying to achieve is a segue that slides off the top. I want the secondVCView to be below the firstVCView and for the firstVCView to slide off displaying the secondVCView.
Currently there is no animation and it just flashes to the secondVCView.
Many Thanks
import UIKit
class TopToBottomSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
var firstVCView = self.sourceViewController.view as UIView!
var secondVCView = self.destinationViewController.view as UIView!
// Get the screen width and height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// Specify the initial position of the destination view.
secondVCView.frame = CGRectMake(0, 0, screenWidth, screenHeight)
// Access the app's key window and insert the destination view below the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, belowSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(0.4, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
//secondVCView.frame = CGRectOffset(secondVCView.frame, -screenWidth, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController,
animated: false,
completion: nil)
}
}
}
As far as I know you need to call layoutIfNeed inside animateWithduration block in order to get a smooth animation.
Greetings
Im not sure if this is the correct way to do this but i managed to get it working by inserting both views.
L
--
import UIKit
class TopToBottomSegue: UIStoryboardSegue {
override func perform() {
// Assign the source and destination views to local variables.
let firstVCView = self.sourceViewController.view as UIView!
let secondVCView = self.destinationViewController.view as UIView!
// Get the screen width, height and status bar height.
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
let statusBarHeight = self.statusBarHeight()
// Specify the initial position of both views.
secondVCView.frame = CGRectMake(0.0, 0.0, screenWidth, screenHeight)
firstVCView.frame = CGRectMake(0.0, statusBarHeight, screenWidth, screenHeight)
// Access the app's key window and insert the destination view below the current (source) one.
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(firstVCView, aboveSubview: secondVCView)
window?.insertSubview(secondVCView, belowSubview: firstVCView)
// Animate the transition.
UIView.animateWithDuration(0.3, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, screenHeight)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as UIViewController,
animated: false,
completion: nil)
}
}
func statusBarHeight() -> CGFloat {
let statusBarSize = UIApplication.sharedApplication().statusBarFrame.size
return Swift.min(statusBarSize.width, statusBarSize.height)
}
}
I'm trying to make custom segue between two view controllers without any animation. Everything works as I expecting just console giving me following message:
2015-06-16 12:19:01.537 prototyp_2[42453:9048104] Unbalanced calls to
begin/end appearance transitions for .
Here is my code:
class Segue_forward: UIStoryboardSegue {
override func perform() {
// zdroj a destinace do lokálních proměnných
var firstVCView = self.sourceViewController.view as UIView!
var secondVCView = self.destinationViewController.view as UIView!
// rozměry displeje
let screenWidth = UIScreen.mainScreen().bounds.size.width
let screenHeight = UIScreen.mainScreen().bounds.size.height
// initial position of destination view
secondVCView.frame = CGRect(x: 0.0, y: 0.0, width: screenWidth, height: screenHeight)
// protože destination VC není subview okna aplikace tak to musime napravit
let window = UIApplication.sharedApplication().keyWindow
window?.insertSubview(secondVCView, aboveSubview: firstVCView)
// animace
UIView.animateWithDuration(0.0, animations: { () -> Void in
firstVCView.frame = CGRectOffset(firstVCView.frame, 0.0, 0.0)
secondVCView.frame = CGRectOffset(secondVCView.frame, 0.0, 0.0)
}) { (Finished) -> Void in
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated: false, completion: nil)
}
}
}
And here is my action:
#IBAction func nextQuestion(sender: AnyObject) {
self.performSegueWithIdentifier("id_forwardSegue", sender: self)
}
Just do it without animation:
override func perform() {
self.sourceViewController.presentViewController(self.destinationViewController as! UIViewController, animated: false, completion: nil)
}
present the new UIViewController with the parentViewController:
self.sourceViewController.parentViewController?.presentViewController(YOUR_VIEW_CONTROLLER, animated: false, completion: nil)