SWIFT slide one Object "in and out" - ios

Is there a way to animate an object in swift (UIView, UIButton, etc.) so that it slides out on the left side of the screen and slides in at the same time on the right side of the screen, that the parts of f.e. an UIView that already slided out on the left side are already sliding in again from the right side.
I hope you can understand my question, its a bit tricky to explain this problem.
I'm using Xcode + Swift 3.0.
Thank you.

that the parts of f.e. an UIView that already slided out on the left side are already sliding in again from the right side
This almost slipped by me. The only way you have one "view" appear in two places on a screen is by really having two views, one on the left and one on the right.
Then, you could set the widthAnchors (properly), hide the subviews (properly), and animate. If the animation is fast enough (my one-sided slide out is set for 0.3 seconds) the user would think it's all one view.

You can use UIView.animate(). Trigger a second animation after the first has completed.
Possible implementation to get the idea:
var container = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 200.0, height: 200.0))
let view = UIView(frame: CGRect(x: 0.0, y: 0.0, width: 50.0, height: 50.0))
view.center = CGPoint(x: 100, y: 100)
view.backgroundColor = UIColor.green
container.addSubview(view)
let screenWidth = CGFloat(356.0)
// animate to left
UIView.animate(withDuration: 2.0, delay: 0.0, options: [], animations: {
view.center = CGPoint(x: -view.frame.size.width, y: view.frame.origin.y)
}, completion: { (finished: Bool) in
//set view to right of screen
view.center = CGPoint(x: view.frame.size.width + screenWidth, y: view.frame.origin.y)
// animate back to center
UIView.animate(withDuration: 2.0, delay: 0.0, options: [], animations: {
view.center = CGPoint(x: 100, y: 100)
})
})

Related

Why the UIView did not rotate(iOS)?

I create a small UIView, and use animate method let it rotate forever:
let v2=UIView(frame: CGRect(x: 100, y: 100, width: 30, height: 30))
v2.backgroundColor=UIColor.orange
v2.transform=CGAffineTransform(rotationAngle: 0)
view.addSubview(v2)
UIView.animate(withDuration: 2, delay: 0, options: [.repeat,.curveLinear], animations: {
v2.transform=CGAffineTransform.init(rotationAngle: -CGFloat(Double.pi*2))
}, completion:nil)
But I found that the UIView did not rotate at all,why?
You are effectively saying "rotate to its current rotation" -- so nothing happens.
Try it like this - rotate to 180-degrees (.pi)... the repeat will then rotate another 180-degrees, and another, and another, etc:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
let v2=UIView(frame: CGRect(x: 100, y: 100, width: 30, height: 30))
v2.backgroundColor=UIColor.orange
view.addSubview(v2)
UIView.animate(withDuration: 2, delay: 0, options: [.repeat,.curveLinear], animations: {
v2.transform = CGAffineTransform(rotationAngle: .pi)
}, completion:nil)
}
Edit - a little more clarification...
If you want a continuous rotation, you can use the above code. Note, though, that if you want a counter-clockwise rotation, you'll need to use a negative rotation angle of less-than 180-degrees.
For example:
UIView.animate(withDuration: 2, delay: 0, options: [.repeat,.curveLinear], animations: {
v2.transform = CGAffineTransform(rotationAngle: -.pi * 0.5)
}, completion:nil)
That will rotate it 90-degrees counter-clockwise, and then again, and again, and again, etc.
On the other hand, if you only want it to rotate once (to any arbitrary angle, clockwise or counter-clockwise), you'll want to use Core Animation as explained in this excellent article by #matt:
Taking Control of Rotation Animations in iOS
Rotated but full cycle 360 degrees
v2.transform=CGAffineTransform.init(rotationAngle: -CGFloat(Double.pi*2))
Change it to this
v2.transform=CGAffineTransform.init(rotationAngle: -CGFloat(Double.pi*2/3))
To verify that rotate happens

Swift || Bug when Animating and Removing Subview from Superview

Im made and DropDown Menu to select Action like the GIF below.
Therefore I made a Subview and animated it in.
When animating the Subview out, it looks really weird.
In particular the problem is that it just looks like a blank small bar and not like the Menu.
Does anyone know where the problem might be?
The ViewController I'm making the Subview of is a simple ViewController with a TableView inside and 1 prototype cell.
Code:
let blackView = UIView()
var tvx: OptionsVC = OptionsVC()
var h: CGFloat!
.
func optionsClicked() {
self.h = CGFloat(70 + (52 * (OptionsVC().arrayFunctionCellNames.count)))
navigationController?.hidesBarsOnTap = false
tvx = self.storyboard?.instantiateViewController(withIdentifier: "options") as! OptionsVC
if let window = UIApplication.shared.keyWindow {
blackView.backgroundColor = UIColor(white: 0, alpha: 0.5)
blackView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleDismiss)))
view.addSubview(blackView)
view.addSubview(tvx.view)
let y = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.height
tvx.view.frame = CGRect(x: 0, y: (y - self.h), width: view.frame.width, height: h)
blackView.frame = CGRect(x: 0, y: y, width: view.frame.width, height: view.frame.height)
blackView.alpha = 0
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.blackView.alpha = 1
self.tvx.view.frame = CGRect(x: 0, y: y, width: self.view.frame.width, height: self.h)
self.blackView.frame = CGRect(x: 0, y: (y + self.h), width: self.view.frame.width, height: self.view.frame.height)
}, completion: nil)
}
}
.
func handleDismiss() {
UIView.animate(withDuration: 5.5, animations: {
let y = (self.navigationController?.navigationBar.frame.size.height)! + UIApplication.shared.statusBarFrame.height
self.blackView.alpha = 0
if let window = UIApplication.shared.keyWindow {
self.blackView.frame = CGRect(x: 0, y: y, width: self.view.frame.width, height: self.view.frame.height)
self.tvx.view.frame = CGRect(x: 0, y: (y - self.h), width: self.view.frame.width, height: self.h)
}
}, completion: {(finished:Bool) in
self.blackView.removeFromSuperview()
self.tvx.view.removeFromSuperview()
self.navigationController?.hidesBarsOnTap = true
})
}
GIF of BUG:
I made it so slow so you can better see it
Edit: My Solution
The problem was the constraints I set on my subview controller. Normally you set them to all sides, in my case, with was really weird I had to set them only to the bottom and sides. If I set some to the top, it would always do this bug.
You haven't really provided enough information for us to understand what's going on. What are tvx, blackView and self? What is the self.h variable? (From the animation it looks like you're changing the height of your "menu" view to be much shorter before you begin the animation code.)
Do you have a view controller contained inside another one using an embed segue?
If so, you should probably animate the constraints on the container view, not the child view controller's view.
As Glenn and D. Greg say in their comments, you should really be adding constraints to your views, hooking up outlets to those constraints, and animating changes to the constraint's constants rather than manipulating your view's frames directly. Animating changes to your view's frames isn't reliable when you're using AutoLayout, since AutoLayout can change your view's size and position out from under your animation code. That code looks like this, (in broad terms, no specific to your code)
myViewAConstraint.constant = someNewValue
myViewBConstraint.constant = someOtherValue
UIView.animate(withDuration: 5.5,
animations: {
someView.alpha = 0 //If you want the view to fade as it animates
self.view.layoutIfNeeded()
},
completion: { (finished:Bool) in
}
)

UIView transform animations not being called

Currently there is a view I'm trying to translate(move), rotate and scale a view at the same time. For so strange reason it's only scaling it when I put scale at the bottom. But when I change the order and put the scaling first it rotates and translates the view properly but the scale of the view changes briefly to its correct scale before changing back to its original size. I need it to stay in it's scaled form. Here is the code:
class ViewController: UIViewController {
let newView = UIView(frame: CGRect(x: 10, y: 10, width: 100, height: 100))
override func viewDidLoad() {
super.viewDidLoad()
self.view.addSubview(newView)
UIView.animate(withDuration: 1.0, animations: {
self.newView.transform = CGAffineTransform(translationX: 50, y: 70)//translation
self.newView.transform = CGAffineTransform(rotationAngle: -CGFloat.pi / 2)//rotation
self.newView.transform = CGAffineTransform(scaleX: 1, y: 0.5)//scale
})
}
}
Try combine the transforms first, then apply them at a time:
let translate = CGAffineTransform(translationX: 50, y: 70)
UIView.animate(withDuration: 1.0, animations: {
self.view.transform = translate.rotated(by: -CGFloat.pi / 2).scaledBy(x: 1, y: 0.5)
})

Custom progress bar animation in ios swift

I'm trying to make a progress bar through UIView consisting of an image with 3 colors, i want the progress bar to move right and reappear from the left border of the screen smoothly. till now i have successfully animated the view, it is moving right and when it reaches the border it disappears and reappears from the left. but i want it to completely go across the right border and keep reappearing from the left at the same time, like if half of the view is gone across the right border, it should have reappeared from the left border at the same instance.
this is what i'm trying.
CustomProgressView.frame = CGRect(x: 0, y: 480, width: 100, height: 20)
UIView.animate(withDuration: 2.0, delay: 0, options: [.repeat, .curveEaseOut], animations: {
//self.CustomProgressView.frame = CGRect(x: 420, y: 480, width: 100, height: 20)
self.CustomProgressView.frame = CGRect(x: 420 , y: 480, width: self.CustomProgressView.frame.size.width,height: self.CustomProgressView.frame.size.height)
}, completion: nil)

Implementation closest to - push the same UIViewController

I am totally aware that you cannot push the same UIViewController from a UINavigationController. However my implementation demands so. I have to swipe gestures. On swiping right to left, I have to reload the content of view (which is a UIScrollView) along with animation. And vice versa on left to right swipe.
I have tried with following but it does not give the expected animation:
func leftSwipe()
{
self.scroll_course_detail.frame = CGRect(x: 2*self.scroll_course_detail!.frame.size.width, y: self.scroll_course_detail.frame.origin.y, width: self.scroll_course_detail.frame.size.width, height: self.scroll_course_detail.frame.size.height)
UIView.animate(withDuration: 0.25, animations: {() -> Void in
self.scroll_course_detail.frame = CGRect(x: 0, y: self.scroll_course_detail.frame.origin.y, width: self.scroll_course_detail.frame.size.width, height: self.scroll_course_detail.frame.size.height)
})
}
func rightSwipe()
{
self.scroll_course_detail.frame = CGRect(x: -self.scroll_course_detail!.frame.size.width, y: self.scroll_course_detail.frame.origin.y, width: self.scroll_course_detail.frame.size.width, height: self.scroll_course_detail.frame.size.height)
UIView.animate(withDuration: 0.25, animations: {() -> Void in
self.scroll_course_detail.frame = CGRect(x: 0, y: self.scroll_course_detail.frame.origin.y, width: self.scroll_course_detail.frame.size.width, height: self.scroll_course_detail.frame.size.height)
})
}
Before animation block, I am changing the frame of UIScrollView which makes the background clear for the animation duration. I was thinking that UIScrollView content is overriden along with animation
So what should be the best approach for reloading content along with animation on swipe ?

Resources