I have got this very simple animation (case):
class ViewController: UIViewController {
var v: UIView!
var b = false
override func viewDidLoad() {
super.viewDidLoad()
self.v = UIView(frame: CGRect(x: 120, y: 250, width: 30, height: 30))
self.v.backgroundColor = .red
self.view.addSubview(self.v)
}
#IBAction func didTapButton(_ sender: UIButton) {
UIView.animate(withDuration: 3,
delay: 0,
options: [.beginFromCurrentState, .curveLinear],
animations: {
if self.b {
self.v.frame = CGRect(x: 120, y: 250, width: 30, height: 30)
} else {
self.v.frame = CGRect(x: 240, y: 250, width: 30, height: 30)
}
self.b = !self.b
},
completion: nil)
}
}
On each tap of the button the red view moves to left or right depending on current direction. The animation should start from the current position and to be linear.
However if I tap the button when the previous animation is already in progress then the red view does not start immediately to move in the opposite direction. It just freezes in the current position while the previous animation finishes and then starts moving. If I change the animation curve option from linear to easeOut and it works properly.
I am using iOS 10, Xcode 8.2.1
Any ideas why this happens?
When the animation is on, the UI components will not react to new changes. It will buffer the input and react once the animation is complete.
This is not related to the iOS or Xcode versions, but a normal behaviour.
I've found the answer.
I tis because of the additive animations in iOS 8 and above. Here is a very useful link which explains what actually happens and why the animation freezes.
http://iosoteric.com/additive-animations-animatewithduration-in-ios-8/
Related
I'm new in Swift programming. I have a problem with an animation of a view when i try to change the text of a UITextView.
This is a method that is triggered from a button that is in the main view. self.parkingMenuTittle is a UITextView that i also have in the main view. When the button is triggered I want to change the text and set the view height to the ycomponent variable that my code has, but the view instead of doing that, goes to the original size. If I don't update the UITextView text my animation works just fine.
#objc func seeMore(_ sender: UIButton){
self.parkingMenuTittle.text = "test"
UIView.animate(
withDuration: 0.2,
animations: {
let frame = self.view.frame
let ycomponent = UIScreen.main.bounds.height-300
self.view.frame = CGRect(x: 0, y: ycomponent, width: frame.width, height: frame.height)
},completion: { (finished: Bool) in
//Code
})
}
I also tried to put the text in the completion block but don't work.
Thanks for your help!
This is my animation code:
UIView.animate(withDuration: 1, delay: 1, options: [.repeat], animations: {
self.micButtonOuterRing?.frame = CGRect(x: (self.originalOuterRingFrame?.origin.x)! - 30, y: (self.originalOuterRingFrame?.origin.y)! - 30, width: (self.originalOuterRingFrame?.width)! + 60, height: (self.originalOuterRingFrame?.height)! + 60)
self.micButtonOuterRing?.alpha = 0
self.micButtonOuterRing?.layer.cornerRadius = (self.micButtonOuterRing?.frame.width)! / 2
}, completion: { _ in
self.micButtonOuterRing?.frame = self.originalOuterRingFrame!
self.micButtonOuterRing?.alpha = 1
})
I am trying to stop it when I press a button so I run the following:
#IBAction func micButtonPressed(_ sender: Any) {
micButtonOuterRing?.layer.removeAllAnimations()
}
But the animation keeps going. I know the micButtonPressed function works because I added a print statement and it worked. Can somebody help?
Use UIPropertyAnimator instead; its designed to let you pause, stop, reverse, etc. and the syntax is almost exactly the same as the old UIViewAnimation Methods.
I am trying to add buttons dynamically to a scroll view after pressing another button.
I created a custom UIView which I want to add to the scroll view.
Below you can find the code how I am trying to do it:
var buttonY: CGFloat = 0 // our Starting Offset, could be 0
for _ in audioCommentsArray {
UIView.animateWithDuration(0.15, animations: {
let commentView = AudioCommentView(frame: CGRectZero)
commentView.frame = CGRect(x: 0, y: buttonY, width: 75, height: 75)
buttonY = buttonY + 75 // we are going to space these UIButtons 75px apart
commentView.alpha = 1.0
audioCommentsListScrollView.addSubview(commentView)
})
}
I want to add these commentView's using a simple animation. However only the last commentView is added correctly to the scrollView whereas the above views are added like this:
Only the background of the commentView is shown whereas the other elements are not visible.
Does anyone have an idea what I might be missing? Adding views using a for loop shouldn't be complicated as I have done this many times before but this time I seem to miss something?
Thank you in advance!
The UIView animations are overlapping and interfering with each other as you process them through the loop. Instead, you should chain them so that the next animation does not interfere with the other. Delete the loop. Then call the animations one after the other in the completion handler. You can call it recursively to ensure each button is animated.
var count = 0
func startButtonsAnimation() {
UIView.animateWithDuration(0.15, animations: {
let commentView = AudioCommentView(frame: CGRectZero)
commentView.frame = CGRect(x: 0, y: buttonY, width: 75, height: 75)
buttonY = buttonY + 75 // we are going to space these UIButtons 75px apart
commentView.alpha = 1.0
audioCommentsListScrollView.addSubview(commentView)
}, completion: { (value: Bool) in
if self.count < self.audioCommentsArray.count {
self.count += 1
self.startButtonsAnimation()
}
})
Recently I learned to make custom in-app keyboards. Now I want to be able to swap between multiple custom keyboards. However, resetting the textField.inputView property does not seem to work.
I recreated a simplified version of this problem in the following project. The UIViews represent actual custom keyboards.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var textField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let blueInputView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 300))
blueInputView.backgroundColor = UIColor.blueColor()
textField.inputView = blueInputView
textField.becomeFirstResponder()
}
#IBAction func changeInputViewButtonTapped(sender: UIButton) {
let yellowInputView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 300))
yellowInputView.backgroundColor = UIColor.yellowColor()
// this doesn't cause the view to switch
textField.inputView = yellowInputView
}
}
Running this gives what I would initially expect: a blue input view pops up.
But when I tap the button to switch to the yellow input view, nothing happens. Why? What do I need to do to get this to work?
After a little more experimenting I have the solution now. I needed to resign the first responder and then set it again. Any first responder that is a subview of the top view can be resigned indirectly by calling endEditing.
#IBAction func changeInputViewButtonTapped(sender: UIButton) {
let yellowInputView = UIView(frame: CGRect(x: 0, y: 0, width: 0, height: 300))
yellowInputView.backgroundColor = UIColor.yellowColor()
// first do this
self.view.endEditing(true)
// or this
//textField.resignFirstResponder()
textField.inputView = yellowInputView
textField.becomeFirstResponder()
}
Thanks to this and this answer for the ideas.
I am creating a popup view that displays on top of a blurred background. It seems to work fine apart from the fact that at the moment, occasionally when it appears, the close button doesn't work at all. This often seems to happen on the second use.
Here is my code:
override func motionEnded(motion: UIEventSubtype, withEvent event: UIEvent) {
if motion == .MotionShake {
println("Shake")
if randomImageContainer.alpha == 0 {
var randomIndex = Int(arc4random_uniform(UInt32(randomImages.count)))
lightBlur = UIBlurEffect(style: UIBlurEffectStyle.Light)
blurRandomView = UIVisualEffectView(effect: lightBlur)
blurRandomView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: view.frame.height)
view.addSubview(blurRandomView)
randomImageContainer.frame = CGRect(x: 0, y: 0, width: 264, height: 308)
randomImageContainer.center.x = view.frame.width/2
randomImageContainer.center.y = view.frame.height/2
randomImageContainer.image = randomImages[randomIndex]
randomImageContainer.contentMode = .ScaleAspectFill
view.addSubview(randomImageContainer)
closeRandomButton.frame = randomImageContainer.frame
closeRandomButton.addTarget(self, action: "closeRandomView:", forControlEvents: UIControlEvents.TouchUpInside)
view.addSubview(closeRandomButton)
view.bringSubviewToFront(closeRandomButton)
UIView.animateWithDuration(1, animations: {
self.randomImageContainer.alpha = 1
self.blurRandomView.alpha = 1
})
}
}
}
func closeRandomView(sender: UIButton!) {
println("Activated")
UIView.animateWithDuration(1, animations: {
self.randomImageContainer.alpha = 0
self.blurRandomView.alpha = 0
}, completion: { (finished:Bool) in
self.blurRandomView.removeFromSuperview()
self.randomImageContainer.removeFromSuperview()
self.closeRandomButton.removeFromSuperview()
})
}
The alpha value for the image and blur view are being set in viewDidLoad(), so they definitely pass the if statement first time around.
I can't see the problem, so I was wondering if anyone could notice any issues?
Thanks.
Figured it out. There was two issues. First of all, I was removing the views from the SuperView which meant they couldn't be checked in the if statement. The other problem was that there was a different view being brought to the front elsewhere in my code which meant the button was unaccessible.