I'm using transitionFromViewController to switch between two view controllers using a segmented control.
Here's my code:
#IBAction func valueChanged(sender: AnyObject) {
var newController = storyboard?.instantiateViewControllerWithIdentifier(viewControllerIdentifiers[sender.selectedSegmentIndex]) as! UIViewController
let oldController = childViewControllers.last as! UIViewController
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
newController.view.frame = oldController.view.frame
if viewControllerIdentifiers[sender.selectedSegmentIndex] == "first" {
let vc = newController as! userProfileViewController
vc.userToShow = self.userToShow
}
transitionFromViewController(oldController, toViewController: newController, duration: 0.25, options: .TransitionCrossDissolve, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
}
is it possible to pass data to the childViewControllers, from the Parent?
Yes
transitionFromViewController(oldController, toViewController: newController, duration: 0.25, options: .TransitionCrossDissolve, animations:{ () -> Void in
newController.property = currentProperty
//basically here is where you can pass the data. I used a property as an example. You know what you need to send :)
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
Related
I have a view that im moving using this
func r() {
movel.constant = self.view.bounds.width - self.move.bounds.width
move.setNeedsLayout()
UIView.animate(withDuration: time, delay: 0, options:[], animations: {
self.move.superview?.layoutIfNeeded()
}, completion: {(finished: Bool) -> Void in
if (finished == true){
self.timerand()
self.l()
}else{
self.view.layoutIfNeeded()
}
})
}
And I'm using these functions to pause and resume it
func movestop() {
pausedmovetime = self.move.layer.convertTime(CACurrentMediaTime(), from: nil)
self.move.layer.speed = 0.0
self.move.layer.timeOffset = pausedmovetime
}
func movestart() {
self.move.layer.speed = 1.0
self.move.layer.timeOffset = 0.0
self.move.layer.beginTime = 0.0
let timeSincePause = self.move.layer.convertTime(CACurrentMediaTime(), from: nil) - pausedmovetime
self.move.layer.beginTime = timeSincePause
}
The app works fine but when I go out of the app I call the movestop() but when I come back it is at its final position and the completion block doesnt get called
I'm attempting to write a protocol and a custom UIStoryboardSegue class that will allow me to easily implement custom transitions in my UIViewControllers:
public protocol TransitionController
{
var transitionDurationIn: CFTimeInterval { get }
var transitionDurationOut: CFTimeInterval { get }
func prepareTransitionIn()
func prepareTransitionOut()
func performTransitionIn(finished: #escaping () -> Void)
func performTransitionOut(finished: #escaping () -> Void)
}
class JFTransitionControllerSegue: UIStoryboardSegue {
override func perform() {
let defaultTransitionDuration : CFTimeInterval = 1.5
if let dvc = self.destination as? TransitionController {
dvc.prepareTransitionIn()
}
else {
// Default transition
self.destination.view.alpha = 0
}
if let svc = self.source as? TransitionController {
svc.prepareTransitionOut()
svc.performTransitionOut(){ () in
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
else
{
// Default transition for the source controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.source.view.alpha = 0
}) { (Finished) in
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
}
}
class TestController: UIViewController, TransitionController {
#IBOutlet weak var form_username: UITextField!
// MARK: - TransitionController Protocol
var transitionDurationIn : CFTimeInterval {return 1.0}
var transitionDurationOut : CFTimeInterval {return 1.0}
func prepareTransitionIn()
{
//self.view.alpha = 0 // no fade in if you uncomment
form_username.alpha = 0 // nil
}
func prepareTransitionOut()
{
self.view.alpha = 1 // works
}
func performTransitionIn(finished: #escaping () -> Void)
{
UIView.animate(withDuration: self.transitionDurationIn, animations: {
//self.view.alpha = 1 // no fade in if you uncomment
self.form_username.alpha = 1 // nil, crashes
}) { (Finished) in
finished()
}
}
func performTransitionOut(finished: #escaping () -> Void)
{
UIView.animate(withDuration: self.transitionDurationOut, animations: {
self.view.alpha = 0 // fades out correctly
}) { (Finished) in
finished()
}
}
}
Basically, you just implement the protocol in any UIViewController you want, then make a segue of class JFTransitionControllerSegue. In the performTransitionIn function, you can just do something like UIView.animate and change the alpha or whatever you like. The problem I'm having is that the destination segue simply pops up instead of transitioning in properly. From what I can tell while debugging it isn't fully initialized - IBOutlet variables are nil, but the controller itself isn't. Is this a bad design pattern, or am I just missing something simple?
A view controller being initialised is one event. It's view being loaded is another.
The view property of a view controller is loaded lazily and the outlets are built and connected at that point. That's why viewDidLoad() is a thing.
If you want the view to be ready for you, you can call loadViewIfNeeded() on the view controller first.
Misdiagnosed the problem...the destination controller was loaded, but I had forgotten to add the destination controller's view to the window in the Segue class:
class JFTransitionControllerSegue: UIStoryboardSegue {
override func perform() {
let defaultTransitionDuration : CFTimeInterval = 1.5
if let dvc = self.destination as? TransitionController {
dvc.prepareTransitionIn()
}
else {
// Default transition
self.destination.view.alpha = 0
}
if let svc = self.source as? TransitionController {
svc.prepareTransitionOut()
svc.performTransitionOut(){ () in
UIApplication.shared.keyWindow?.insertSubview(self.destination.view, aboveSubview: self.source.view)
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
else
{
// Default transition for the source controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.source.view.alpha = 0
}) { (Finished) in
UIApplication.shared.keyWindow?.insertSubview(self.destination.view, aboveSubview: self.source.view)
if let dvc = self.destination as? TransitionController {
dvc.performTransitionIn(){ () in
self.source.present(self.destination, animated: false, completion: nil)
}
}
else {
// Default transition for the destination controller
UIView.animate(withDuration: defaultTransitionDuration, animations: {
self.destination.view.alpha = 1
}) { (Finished) in
self.source.present(self.destination, animated: false, completion: nil)
}
}
}
}
}
}
So, I have a segmented control to switch between to view controllers.
However, often, as soon as I switch, I get this message:
Unbalanced calls to begin/end appearance transitions
Plus, after I get this, sometimes it happens that every object on the view disappear.
Here's the code:
func segmentValueChanged(sender: AnyObject) {
if sender.selectedIndex == 0 {
let newController = controllers.newController1
let oldController = childViewControllers.last as UIViewController!
self.container.frame.size.height = newController.view.frame.height
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
transitionFromViewController(oldController, toViewController: newController, duration: 0.9, options: .CurveLinear, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
} else if sender.selectedIndex == 1 {
let newController = controllers.newController2
let oldController = childViewControllers.last as UIViewController!
oldController.willMoveToParentViewController(nil)
addChildViewController(newController)
transitionFromViewController(oldController, toViewController: newController, duration: 0.9, options: .CurveLinear, animations:{ () -> Void in
// nothing needed here
}, completion: { (finished) -> Void in
oldController.removeFromParentViewController()
newController.didMoveToParentViewController(self)
})
}
}
How can I solve this?
This line is backwards:
self.container.frame.size.height = newController.view.frame.height
It is newController's view's frame that you need to set!
Moreover, you are failing to put newController.view into the interface. That is why you end up without an interface.
I want to set speed to my UISlider, to make it move more smoothly.
Here is how I am trying to animate it :
UIView.animateWithDuration(6, delay: 0.0, options: UIViewAnimationOptions.TransitionCurlUp, animations: { () -> Void in
self.videoSlider.setValue(time, animated: true)
if self.videoSlider.value == self.videoSlider.maximumValue {
self.playerLayer.stop()
self.playBigButton.hidden = false
self.pauseButton.hidden = true
self.playButton.hidden = false
}
}, completion: { (finished: Bool) -> Void in
})
But it moves with a different speed on the beginning, middle and end.
Try giving your animation the option of .CurveLinear, like this:
UIView.animateWithDuration(6,
delay: 0.0,
options: [UIViewAnimationOptions.TransitionCurlUp, .CurveLinear],
animations: { () -> Void in
self.videoSlider.setValue(time, animated: true)
if self.videoSlider.value == self.videoSlider.maximumValue {
self.playerLayer.stop()
self.playBigButton.hidden = false
self.pauseButton.hidden = true
self.playButton.hidden = false
}
}, completion: { (finished: Bool) -> Void in
})
I am trying to make a custom segue and it keeps telling me that I am using an ambiguous use of 'view'. Can anyone explain what's going on?
Code:
import UIKit
class customSegue: UIStoryboardSegue {
override func perform() {
let sourceVC: AnyObject = self.sourceViewController
let destinationVC: AnyObject = self.destinationViewController
sourceVC.view.addSubview(destinationVC.view)
destinationVC.view.transform = CGAffineTransformMakeScale(0.05, 0.05)
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseInOut, animations: { () -> Void in
destinationVC.view.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (finished) -> Void in
destinationVC.view.removeFromSuperview()
let time = dispatch_time(DISPATCH_TIME_NOW, Int64(0.001 * Double(NSEC_PER_SEC)))
dispatch_after(time, dispatch_get_main_queue()) {
sourceVC.presentViewController(destinationVC, animated: false, completion: nil)
}
}
}
}
The 3 Lines of code that give error - Ambiguous use of 'view' :
sourceVC.view.addSubview(destinationVC.view)
destinationVC.view.transform = CGAffineTransformMakeScale(0.05, 0.05)
destinationVC.view.transform = CGAffineTransformMakeScale(1.0, 1.0)
The reason you are getting these errors is that you are typing your sourceVC and destinationVC variables as AnyObject. You must type your variables as a UIViewController so swift can see the correct view attribute. Your variable declaration would then look like this:
let sourceVC: UIViewController = self.sourceViewController
let destinationVC: UIViewController = self.destinationViewController