My custom segue animation works fine, but sometimes I see white-flashes/flickering during the animation. Any tips or suggestions on how to avoid this?
This is my code :
import UIKit
#objc(InsetBlurModalSeque) class InsetBlurModalSeque: UIStoryboardSegue {
override func perform() {
let sourceViewController = self.sourceViewController as UIViewController
let destinationViewController = self.destinationViewController as UIViewController
// Make sure the background is ransparent
destinationViewController.view.backgroundColor = UIColor.clearColor()
// Take screenshot from source VC
UIGraphicsBeginImageContext(sourceViewController.view.bounds.size)
sourceViewController.view.drawViewHierarchyInRect(sourceViewController.view.frame, afterScreenUpdates:true)
var image:UIImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
// Blur screenshot
var blurredImage:UIImage = image.applyBlurWithRadius(5, tintColor: UIColor(white: 1.0, alpha: 0.0), saturationDeltaFactor: 1.3, maskImage: nil)
// Crop screenshot, add to view and send to back
let blurredBackgroundImageView : UIImageView = UIImageView(image:blurredImage)
blurredBackgroundImageView.clipsToBounds = true;
blurredBackgroundImageView.contentMode = UIViewContentMode.Center
let insets:UIEdgeInsets = UIEdgeInsetsMake(20, 20, 20, 20);
blurredBackgroundImageView.frame = UIEdgeInsetsInsetRect(blurredBackgroundImageView.frame, insets)
destinationViewController.view.addSubview(blurredBackgroundImageView)
destinationViewController.view.sendSubviewToBack(blurredBackgroundImageView)
// Add original screenshot behind blurred image
let backgroundImageView : UIImageView = UIImageView(image:image)
destinationViewController.view.addSubview(backgroundImageView)
destinationViewController.view.sendSubviewToBack(backgroundImageView)
// Add the destination view as a subview, temporarily – we need this do to the animation
sourceViewController.view.addSubview(destinationViewController.view)
// Set initial state of animation
destinationViewController.view.transform = CGAffineTransformScale(CGAffineTransformIdentity, 0.1, 0.1);
blurredBackgroundImageView.alpha = 0.0;
backgroundImageView.alpha = 0.0;
// Animate
UIView.animateWithDuration(0.5,
delay: 0.0,
usingSpringWithDamping: 0.6,
initialSpringVelocity: 0.0,
options: UIViewAnimationOptions.CurveLinear,
animations: {
destinationViewController.view.transform = CGAffineTransformIdentity
blurredBackgroundImageView.alpha = 1.0
backgroundImageView.alpha = 1.0;
},
completion: { (fininshed: Bool) -> () in
// Remove from temp super view
destinationViewController.view.removeFromSuperview()
sourceViewController.presentViewController(destinationViewController, animated: false, completion: nil)
}
)
}
}
Thanks.
Related
Inside a UIViewController, I call a .xib file and present it over the current UIView.
// initiate the pop up ad view and display it
let popUpAdView = PopUpAdViewController(nibName: "PopUpAdView", bundle: nil)
popUpAdView.displayIntoSuperView(view)
There is a button inside that .xib file that should remove itself from the screen when it's touched. However it doesn't perform so.
What exactly am I missing?
class PopUpAdViewController: UIViewController {
#IBOutlet weak var popUpAdView: UIView!
#IBOutlet weak var closeButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
// adjust the view size from the device's screen size
let screenSize = UIScreen.mainScreen().bounds
view.frame = CGRectMake(0, 64, screenSize.width, screenSize.height-64)
// cosmetically adjust the view itself
view.backgroundColor = UIColor.blackColor().colorWithAlphaComponent(0.6)
view.userInteractionEnabled = true
closeButton.userInteractionEnabled = true
// style the pop up ad view
popUpAdView.layer.cornerRadius = 5
popUpAdView.layer.shadowOpacity = 0.8
popUpAdView.layer.shadowOffset = CGSizeMake(0.0, 0.0)
closeButton.addTarget(self, action: #selector(removeOutOfSuperView), forControlEvents: .TouchUpInside)
}
func displayIntoSuperView(superView: UIView!) {
superView.addSubview(self.view)
// define the initial cosmetical values of the items
popUpAdView.transform = CGAffineTransformMakeScale(0.3, 0.3)
popUpAdView.alpha = 0.0
closeButton.alpha = 0.0
// animate...
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.popUpAdView.alpha = 1.0
self.popUpAdView.transform = CGAffineTransformMakeScale(1.0, 1.0)
}) { (Bool) in
UIView.animateWithDuration(0.6, delay: 1.5, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 1.0
}, completion: { (Bool) in
})
}
}
func removeOutOfSuperView() {
UIView.animateWithDuration(0.5, delay: 0.0, options: .CurveEaseIn, animations: {
self.closeButton.alpha = 0.0
self.popUpAdView.transform = CGAffineTransformMakeScale(0.1, 0.1)
}) { (finished) in
UIView.animateWithDuration(0.8, delay: 0.0, options: .CurveEaseIn, animations: {
self.view.alpha = 0.0
self.view.removeFromSuperview()
}, completion: nil)
}
}
#IBAction func closePopUpAdView(sender: AnyObject) {
print("Closing the pop up ad...")
removeOutOfSuperView()
}
}
Update
.xib structureL:
I tried to follow this question: Presenting a UINavigationController in a custom modal, but my navigation bar is shrinking from 64p to 44p after the animation. I'm using a custom UIPresentationController and UIViewControllerAnimatedTransitioning.
I think the problem is with the following code. When I comment out the animation it the navigation bar starts out and remains at 44p
func animatePresentationWithTransitionContext(transitionContext: UIViewControllerContextTransitioning) {
let presentedController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)! as DrawerNavigationController
let presentedControllerView = transitionContext.viewForKey(UITransitionContextToViewKey)!
let containerView = transitionContext.containerView()
var finalFrame = transitionContext.finalFrameForViewController(presentedController)
presentedControllerView.frame = CGRectOffset(finalFrame, 0, containerView.bounds.size.height)
containerView.addSubview(presentedControllerView)
UIView.animateWithDuration(self.duration, delay: 0.0, usingSpringWithDamping: 1.0, initialSpringVelocity: 0.0, options: .AllowUserInteraction, animations: {
var height = presentedController.height()
presentedControllerView.frame.origin.y = containerView.bounds.size.height - height
}, completion: {(completed: Bool) -> Void in
transitionContext.completeTransition(true)
})
}
I have the following code that will animate some UITableViewCells from the bottom in a spring manner:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
self.tableView.reloadData()
let cellsNum = self.tableView.visibleCells() as NSArray
// Hide the tableView and move the cells to below the tableView
self.tableView.alpha = 0.0
for cell in cellsNum as [UITableViewCell] {
cell.transform = CGAffineTransformMakeTranslation(0, self.tableView.bounds.size.height)
println("\(NSStringFromCGAffineTransform(cell.transform))")
//cell.layer.transform = CATransform3DRotate(CATransform3DIdentity, CGFloat(90.0) * CGFloat(M_PI) / CGFloat(180.0), 1.0, 0.0, 0.0)
}
// Show the tableView and animate the cells
self.tableView.alpha = 1.0
for cell in cellsNum as [UITableViewCell] {
UIView.animateWithDuration(1.2, delay: 0.05 * Double(cellsNum.indexOfObject(cell)), usingSpringWithDamping: 0.77, initialSpringVelocity: 0, options: nil, animations: { () -> Void in
cell.transform = CGAffineTransformMakeTranslation(0, 0)
//cell.layer.transform = CATransform3DRotate(CATransform3DIdentity, 0, 1.0, 0.0, 0.0)
}, completion: nil);
}
}
How can I do this in Facebook Pop? I've looked in the source but the closest one was kPopLayerTranslationXY but that the code below didn't work:
for cell in cellsNum as [UITableViewCell] {
let trans = POPSpringAnimation(propertyNamed: kPOPLayerTranslationXY)
trans.toValue = NSValue(CGPoint: CGPointMake(0, 0))
trans.fromValue = NSValue(CGPoint: CGPointMake(0, self.tableView.bounds.size.height))
trans.springBounciness = 10
trans.springSpeed = 5
cell.pop_addAnimation(trans, forKey: "Translation")
}
Possibly define a custom animatable property?
You are animating a layer property so you should add the animation to the layer of the cell, not the cell.
cell.layer.pop_addAnimation(trans, forKey: "Translation")
Also as you are only animating y you could use kPOPLayerTranslationY and set the toValue and fromValue to a float.
I'm presenting a view from another using a UIViewControllerTransitioningDelegate instance as transitioning delegate and modalPresentationStyle = Custom.
I'm using a TableViewController using static table view cells with UITextfields inside. Now when tapping over a text field whose borders are close to the the keyboard's frame, part of the tableView beneath the keyboard shows up. I also removed a UITapGestureRecognizer that I added to the semi-transparent background to make sure it's not part of the problem but the issue it's still there. Any ideas? Below is the animateTransition() method
func animateTransition(transitionContext: UIViewControllerContextTransitioning) {
dimmingView.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
let containerView = transitionContext.containerView()
if let presentedViewController = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey) {
let presentedView = presentedViewController.view
let fromView = transitionContext.viewForKey(UITransitionContextFromViewControllerKey)
let centre = presentedView.center
if isPresenting {
presentedView.center = containerView.center
presentedView.frame = presentedView.bounds.rectByInsetting(dx: 30, dy: 150)
transitionContext.containerView().addSubview(presentedView)
dimmingView.frame = containerView.bounds
dimmingView.alpha = 0.0
containerView.insertSubview(dimmingView, atIndex: 0)
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({
context in
self.dimmingView.alpha = 1.0
}, completion: nil)
}
else {
presentedViewController.transitionCoordinator()?.animateAlongsideTransition({
context in
self.dimmingView.alpha = 0.0
}, completion: {
context in
self.dimmingView.removeFromSuperview()
})
}
UIView.animateWithDuration(self.transitionDuration(transitionContext),
delay: 0, usingSpringWithDamping: 0.6, initialSpringVelocity: 10.0, options: nil,
animations: {
presentedView.center = centre
}, completion: {
_ in
transitionContext.completeTransition(true)
})
}
}
The effect could be implemented like the following code in animateTransition method:
UIView.animateWithDuration(duration,
delay: 0,
usingSpringWithDamping: 0.3,
initialSpringVelocity: 0.0,
options: .CurveLinear,
animations: {
fromVC.view.alpha = 0.5
toVC.view.frame = finalFrame
},
completion: {_ -> () in
fromVC.view.alpha = 1.0
transitionContext.completeTransition(true)
})
But how could I implement it using gravity and collision behaviors(UIGravityBehavior, UICollisionBehavior)?
And a more general question may be "How to use the UIDynamicAnimator to customize the transitions between UIViewControllers?"
You can find the solution under the post Custom view controller transitions with UIDynamic behaviors by dasdom.
And the Swift code:
func transitionDuration(transitionContext: UIViewControllerContextTransitioning!) -> NSTimeInterval {
return 1.0
}
func animateTransition(transitionContext: UIViewControllerContextTransitioning!) {
// 1. Prepare for the required components
let toVC = transitionContext.viewControllerForKey(UITransitionContextToViewControllerKey)
let finalFrame = transitionContext.finalFrameForViewController(toVC)
let containerView = transitionContext.containerView()
let screenBounds = UIScreen.mainScreen().bounds
// 2. Make toVC at the top of the screen
toVC.view.frame = CGRectOffset(finalFrame, 0, -1.0 * CGRectGetHeight(screenBounds))
containerView.addSubview(toVC.view)
// 3. Set the dynamic animators used by the view controller presentation
var animator: UIDynamicAnimator? = UIDynamicAnimator(referenceView: containerView)
let gravity = UIGravityBehavior(items: [toVC.view])
gravity.magnitude = 10
let collision = UICollisionBehavior(items: [toVC.view])
collision.addBoundaryWithIdentifier("GravityBoundary",
fromPoint: CGPoint(x: 0, y: screenBounds.height),
toPoint: CGPoint(x: screenBounds.width, y: screenBounds.height))
let animatorItem = UIDynamicItemBehavior(items: [toVC.view])
animatorItem.elasticity = 0.5
animator!.addBehavior(gravity)
animator!.addBehavior(collision)
animator!.addBehavior(animatorItem)
// 4. Complete the transition after the time of the duration
let nsecs = transitionDuration(transitionContext) * Double(NSEC_PER_SEC)
let delay = dispatch_time(DISPATCH_TIME_NOW, Int64(nsecs))
dispatch_after(delay, dispatch_get_main_queue()) {
animator = nil
transitionContext.completeTransition(true)
}
}
A little more complicated than using animateWithDuration:delay:usingSpringWithDamping:initialSpringVelocity:options:animations:completion: method.
EDIT: Fixed a bug when 'transitionDuration' is ≤1