Imagine each view on the screen is to show some information about that particular day. Swiping right advances the date by one day, swiping left shows yesterday's data.
I've managed to achieve this by capturing the swipe gesture and updating the data on the screen, but I don't get the swipe animation. I'm guessing the proper way to do this is to segue the view controller to itself and update instead but I'm not sure. Can anyone advise on the proper way to do this?
You need a block like this for each direction. This one is for Swipe Right (go back). The other direction is reversed.
UIView.animateWithDuration(NSTimeInterval(0.1), animations: {
//swipes view off screen
self.calView.frame.origin.x = self.view.frame.width
})
UIView.animateWithDuration(NSTimeInterval(0), delay: NSTimeInterval(0.1), options: UIViewAnimationOptions(rawValue: 0), animations: {
//jumps to other side
self.calView.frame.origin.x = -self.view.frame.width
}, completion: { _ in
//reload data, subtract one, whatever
self.setCurrentDate()
self.collectionView?.reloadData()
})
UIView.animateWithDuration(NSTimeInterval(0.1), delay: NSTimeInterval(0.2), options: UIViewAnimationOptions(rawValue: 0), animations: {
//swipes back to center
self.calView.frame.origin.x = 0
}, completion: nil)
Related
I have UITableView that update rapidly via WebSocket and RXSwift. Every update will play flash animation. Everything works well in iOS11 - iOS14 but after the iOS15 update, the animation has weird behavior. It isn't play properly. It skip most of animation updates. Sometime it play animation in all rows at the same time.
Edited: I've got another issue; when I press on the button in the cell, the action didn't fire. It take a lot of click on it to make it fired, looks like it can't touch the button while updating. Sometime I press on button in cell 1 but the action fired as cell 2 context.
(Cell information was hidden for secret)
From the video, on the left was run on iOS11-iOS14. The animation works smoothly while on the right the animation was skipped.
The code to update the animation is:
func flash()->Observable<Void>{
return Observable.create { (observer) -> Disposable in
UIView.animateKeyframes(withDuration: 0.5, delay: 0, options: []){
UIView.addKeyframe(withRelativeStartTime: 0, relativeDuration: 0.5) {
self.background.alpha = 0
}
UIView.addKeyframe(withRelativeStartTime: 0.25, relativeDuration: 0.5) {
self.background.alpha = 0.2
}
UIView.addKeyframe(withRelativeStartTime: 1, relativeDuration: 0.5) {
self.background.alpha = 0
}
}
return Disposables.create()
}
}
Call it like this. I didn't dispose someBehaviorRelay because it removes the animation
someBehaviorRelay.subscribe { [unowned self] value in
flash().subscribe().disposed(by: disposeBag)
}
And I reassign disposeBage when reuse
override func prepareForReuse() {
disposeBag = DisposeBag()
}
Is there any suggestion for me to solve this problem? Thank you.
UPDATE
I found the solution is use reconfigure instead of reloadData for UITableView
// if iOS15
let indexPaths = tableView.indexPathsForVisibleRows!
if !indexPaths.isEmpty {
tableView.reconfigureRows(at: indexPaths)
} else {
tableView.reloadData()
}
I found the solution already, updated in the question.
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 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! ;)
I'm trying to show a custom view when I receive a notification from parse.
This view is showed with an animation and its hidden with another animation. This view also has a uitapgesturerecognizer that needs to be fired when the user taps the view.
My problem is that when the second animation gets fired the custom view's uitapgesture doesn't work :\
Any ideas? I paste the code.
Thanks!
func doManageObjectNotification(notification: COPushNotification){
mainView = UIApplication.sharedApplication().keyWindow!
customView = CustomNotificationView()
let height = customView.calculateViewHeight()
customView.frame = CGRectMake(0, -height, mainView.frame.width, height)
customView.targetMethod = notificationWasTapped
customView.setContent(notification)
customView.alpha = 0
mainView.addSubview(customView)
UIView.animateWithDuration(0.75, delay: 0, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: { () -> Void in
// Show the view
self.customView.frame.origin.y = 0
self.customView.alpha = 1
}) { (Bool) -> Void in
UIView.animateWithDuration(0.75, delay: 5, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: {
// Hide the view
self.customView.alpha = 0
}, completion: { finished in
self.customView.removeFromSuperview()
self.customView = nil
})
}
}
Im agree with Lion answer, but I want also focus your attention about customView.frame.origin.y = 0 during animation: if you use autolayout and you try to change frame dimensions or positions instead the correct constraints involved, you can disable you constraints effect causing warnings and unexpected view dimensions and movements. When you have this issue many times the UITapGestureRecognizer stop to responding.
The best way to do it is to create IBOutlets constraints and working with it (for example):
#IBOutlet weak var customViewTopConstraint: NSLayoutConstraint
...
UIView.animateWithDuration(0.75, delay: 0, options: [UIViewAnimationOptions.CurveEaseInOut, UIViewAnimationOptions.AllowUserInteraction], animations: { () -> Void in
// Show the view
self.customViewTopConstraint.constant = 0
For handling tap during animation you should implement touchesBegan method. by this you can detect touch and then check that touch is from your view,if yes then you can perform your desired task.
Hope this will help :)
This is iPad app by using SWIFT. It having two views.
1. starting_View
2. login_View
These two views are in same ViewController.
starting_View will be first view. By Clicking, NEXT button in first View, Starting View will move to left side by using animateDuration and same time, login_View will come from right. If we click Username/password fields (Any TextField), it will navigates to previous view.
Same time,,
if login_View will be first screen means, textField is working, keyboard is appearing.
But in animateDuration, i couldn't type. Kindly help me. Am using XCODE 6.1.
Code (from comment):
#IBAction func getStart_button(sender: UIButton) {
UIView.animateWithDuration(0.7, delay: 0.25, options: .CurveEaseOut, animations: {
self.clt_login_vw.frame = CGRectMake(450, 56, 574, 660)
}, completion:nil)
}
In General try to never change frames directly when using Autolayout.
The best way to move views have been for me to use CGAffineTransform.
let move = CGAffineTransformMakeTranslation(translation_X , translation_Y)
and inside your animation closure:
self.clt_login_vw.transform = move
Then to return it back just do inside the animation Closure:
self.clt_login_vw.transform = CGAffineTransformIdentity
Resume:
let moveOut = CGAffineTransformMakeTranslation(translation_X , translation_Y)
#IBAction func getStart_button(sender: UIButton) {
UIView.animateWithDuration(0.7, delay: 0.25, options: .CurveEaseOut, animations: {
[weak self] // Remember no not create cycles
self!.clt_login_vw.transform = moveOut
}, completion:nil)
}
Edit:
Im gonna add Noah Witherspoon's option.
Another way to do this is getting the constraint that is holding your view horizontally, once you get it, you change its constant and call layoutIfNeeded() inside the closure:
#IBAction func getStart_button(sender: UIButton) {
constraint.constant = newConstant // enough to make it off the screen
UIView.animateWithDuration(0.7, delay: 0.25, options: .CurveEaseOut, animations: {
[weak self] // Remember no not create cycles
self!.view.layoutIfNeeded()
}, completion:nil)
}
The way to get the specific constraint, I'm more friend of making IBOutlets, so I get easily its reference in Code.
That is if you didn't make it by code.