I am having trouble creating a custom segue using swift 3 and iOS 8. I am trying to transition between view controllers by fading from one VC to a black screen and then fading from black to my second VC. I tried to implement the segue by creating a custom segue using the code below, but it is not working as I would like it to. My goal is to perform an animation when the black square goes from 0.5 alpha to 1.0 alpha, then present my second view controller, then set the black square from 1.0 alpha back to 0.5 alpha and delete it. Right now, it does the first part correctly but after the animation finishes you can see the first VC for a brief instant before the second VC pops up. How should I change my code to make the transition smoother and get the desired result?
override func perform() {
let sourceVC = self.source
let destinationVC = self.destination
let square = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
square.alpha = 0.5
square.backgroundColor = UIColor.black
sourceVC.view.addSubview(square)
UIView.animate(withDuration: 0.2, animations: {
square.alpha = 1.0
})
UIView.animate(withDuration: 0.2, delay: 0.2, animations: {
square.alpha = 0.5
}) { (finished) in
sourceVC.present(destinationVC, animated: false, completion: nil)
}
}
Update your code like below and try:
UIView.animate(withDuration: 0.2, animations: {
square.alpha = 1.0
UIView.animate(withDuration: 0.2, delay: 0.2, animations: {
square.alpha = 0.5
}) { (finished) in
DispatchQueue.main.async {
sourceVC.present(destinationVC, animated: false, completion: nil)
}
})
}
Related
I'm working on a project to learn animations and am having trouble using the completion block for the UIView.animate(withDuration:) func. MY animation is a shoebox that falls from the top of the screen, lands on a pedestal, then opens. Once the box opens I want a UIImageView to come out of the box, grow to full size of the screen and then segue to the next page, but the completion handler that the code for segueing is called before my animation completes and the UIImageView doesn't appear at all.
Here's my code:
override func viewDidAppear(_ animated: Bool) {
UIView.animate(withDuration: 1.5, delay: 0.0, options: .curveEaseInOut,
animations: {
//Opening the box
self.shoeBoxImage.shoeBox.animationImages = self.boxOpeningAnimation
self.shoeBoxImage.shoeBox.animationDuration = 1.5
self.shoeBoxImage.shoeBox.animationRepeatCount = 1
self.shoeBoxImage.shoeBox.contentMode = .scaleAspectFill
self.shoeBoxImage.shoeBox.startAnimating()
//set to the final image
self.shoeBoxImage.shoeBox.image = UIImage(named: "frame13")
},completion: {_ in
let nextPage = UIImageView()
nextPage.frame = CGRect(origin: self.shoeBoxImage.center, size: CGSize(width: 0.0, height: 0.0))
nextPage.image = UIImage(named: "FirstLoadBackgroundImg.jpeg")
nextPage.autoresizesSubviews = true
self.view.addSubview(nextPage)
self.view.bringSubviewToFront(nextPage)
UIView.animate(withDuration: 5.0, animations: {
nextPage.transform = CGAffineTransform(scaleX: 428, y: 926)
})
self.performSegue(withIdentifier: "FinishedLoading", sender: self)
})
}
This is my first time working with animations and programatically creating views so if someone could explain how to make the completion block wait for the animation to complete. In order to make the UIImageView appear and animate then once it's full screen, segue to the next page it would be very much appreciated.
The size is 0, 0. Transforming zero by any scale is still zero. I would advise you to not use transform at all, but rather just set the final frame to be what you want.
E.g.,
let startFrame = CGRect(x: view.bounds.midX, y: view.bounds.midY, width: 0, height: 0)
let endFrame = view.bounds
let imageView = UIImageView(image: ...)
imageView.contentMode = .scaleAspectFill
view.addSubview(imageView)
imageView.frame = startFrame
UIView.animate(withDuration: 3, delay: 0, options: .curveEaseInOut) {
imageView.frame = endFrame
} completion: { _ in
// do something here
}
That yields:
By the way, the performSegue probably should be inside a completion closure of the inner animate call.
I have this code....
UIView.transition(with: window!, duration: 0.3, options: **animation**, animations: {
window!.rootViewController = rootVC
}, completion: nil
Is it possible put Hero transition replacing "animation" var?
As an example:
let current = window.rootViewController!
let transition = HeroTransition()
// Collapse vertically in dismissal, over 1 second.
current.view.hero.modifiers = [.scale(x: 1, y: 0.1, z: 1), .duration(1), .fade]
// Rotate in, over 2 seconds.
replacement.view.hero.modifiers = [.rotate(z:1.6), .duration(2), .fade]
transition.transition(from: current, to: replacement, in: window) { (finished) in
window.rootViewController = replacement
}
I'm trying to fade out a UILabel which has been added as a UIViewController's navigation item's titleView. On iOS 11 and below, the following code correctly fades out the label. When run on iOS 12, the fade in animation works, but the fade out does not animate - the view disappears immediately.
let fadeTextAnimation = CATransition()
fadeTextAnimation.type = .fade
fadeTextAnimation.duration = 0.5
navigationItem.titleView!.layer.add(fadeTextAnimation, forKey: nil)
(navigationItem.titleView as! UILabel).isHidden = didShowNavigationItemTitle
I've also tried changing the code to use UIView.transition, but the same behaviour is observed.
UIView.transition(with: navigationItem.titleView!,
duration: 0.5,
options: .transitionCrossDissolve,
animations: {
(self.navigationItem.titleView as! UILabel).isHidden = self.didShowNavigationItemTitle
}, completion: nil)
How can one achieve this fade out in iOS 12?
Just use a UIView.animate block and animate the alpha of the title view.
UIView.animate(withDuration: 0.5, delay: 0, options: .transitionCrossDissolve, animations: {
self.navigationItem.titleView?.alpha = 0
})
I see in some apps when you come to a screen with a tableview there's a short animation of the cell starting to be swiped, showing the red "swipe to delete" button (UIContextualAction button) and then it returns to normal. It is giving the user the hint: "These rows can be swiped."
Is there a way to achieve this effect? Maybe a way to programmatically start a row swipe then cancel it?
Swift Solution
Well, about 1.5 years later I finally came up with a solution.
Step 1 - Cell UI Setup
I set up my custom table view cell like this:
A and B represent the swipe action colors.
C is the UIView that I will animate side-to-side.
Step 2 - Add Animation to Cell
func animateSwipeHint() {
slideInFromRight()
}
private func slideInFromRight() {
UIView.animate(withDuration: 0.5, delay: 0.3, options: [.curveEaseOut], animations: {
self.cellBackgroundView.transform = CGAffineTransform(translationX: -self.swipeHintDistance, y: 0)
self.cellBackgroundView.layer.cornerRadius = 10
}) { (success) in
UIView.animate(withDuration: 0.2, delay: 0, options: [.curveLinear], animations: {
self.cellBackgroundView.transform = .identity
}, completion: { (success) in
// Slide from left if you have leading swipe actions
self.slideInFromLeft()
})
}
}
private func slideInFromLeft() {
UIView.animate(withDuration: 0.5, delay: 0, options: [.curveEaseOut], animations: {
self.cellBackgroundView.transform = CGAffineTransform(translationX: self.swipeHintDistance, y: 0)
}) { (success) in
UIView.animate(withDuration: 0.2, delay: 0, options: [.curveLinear], animations: {
self.cellBackgroundView.transform = .identity
})
}
}
Step 3 - Trigger the Animation
In the viewDidLoad of the view controller that has the table view, I have this code:
if self.tableView.visibleCells.count > 0 {
let cell = self.tableView.visibleCells[0] as! TableViewCell
cell.animateSwipeHint()
}
Example:
Video Solution
I created a video if you'd like a more in-depth walkthrough of this solution:
https://youtu.be/oAGoFd_GrxE
I have a piece of code that I saw long time ago to animate a view. Since our UITableViewCell is also a view, we can use it :) You just need to get your visible cell to animate, like so:
if let visibleCell = self.tableView.cellForRow(at: IndexPath(row: 0, section: 0)) as? CustomCell {
print("Started animation...")
let animation = CAKeyframeAnimation(keyPath: "transform.translation.x")
animation.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionLinear)
animation.duration = 0.6
animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0 ]
visibleCell.layer.add(animation, forKey: "shake")
}
Let me know if this helps. Tested it.
EDIT:
Animating your UITableView to let the user see that they can swipe on a cell is pretty easy, try it like so:
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
self.tableView.setEditing(true, animated: true)
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
self.tableView.setEditing(false, animated: true)
}
}
HOWEVER, if you want to swipe programmatically your cell to show your custom row actions, (I've been researching this for an hour), you can only achieve this, as far as I know, by using method swizzling. See this SO answer: http://codejaxy.com/q/186524/ios-swift-uitableview-how-to-present-uitableviewrowactions-from-pressing-a-button
I have a view which animates from the bottom of my ViewController, and I'm trying to get the animation to be the same as the default
present(viewController, animated:true, completion:nil)
I want to animate a view with the same easing and timing as the above code. Right now I'm using this:
UIView.animate(withDuration: 0.35, delay: 0, options: .curveEaseInOut, animations: {
self.myView.frame = newFrame
}, completion: nil)
The animations aren't similar though, and I don't know what values to use in my UIView animation to make it look like a viewController present. Anyone know what values could get me close?
here is sample code with somewhat animation like presenting viewcontroller
var subViewNew = UIView()
make initial dream setup of you subview which you want to make to add on main view as below
override func viewDidLoad() {
super.viewDidLoad()
subViewNew.frame = CGRect(x: 100,y: 100,width:self.view.frame.width - 200,height:200)
self.subViewNew.frame.origin.y = self.view.frame.origin.y + self.view.frame.size.height
subViewNew.backgroundColor = UIColor.gray
}
for appearance of the view subview you can go like this
UIView.animate(withDuration: 0.7, animations: {
self.subViewNew.frame.origin.y = 300 + 20
self.view.addSubview(self.subViewNew)
})
for dismiss or hiding it you can go like this
UIView.animate(withDuration: 0.7, animations: {
self.subViewNew.frame.origin.y = self.view.frame.origin.y + self.view.frame.size.height
self.subViewNew.removeFromSuperview() //If you want then only
})
Hope this help you