I'm trying to animate the change of an image in a UIImageView using transitionWithVIew. The image changes, but it doesn't seem to be animating. Not sure why this is happening.
Here's my code:
func changeBackgroundAtIndex(index : Int) {
switch index {
case 0:
animateChangeWithImage(MLStyleKit.imageOfAboutMe)
case 1:
animateChangeWithImage(MLStyleKit.imageOfProjects)
case 2:
animateChangeWithImage(MLStyleKit.imageOfSkills)
case 3:
animateChangeWithImage(MLStyleKit.imageOfEducation)
case 4:
animateChangeWithImage(MLStyleKit.imageOfTheFuture)
default:
animateChangeWithImage(MLStyleKit.imageOfAboutMe)
}
}
func animateChangeWithImage(image : UIImage) {
UIView.transitionWithView(backgroundImageView, duration: 0.4, options: UIViewAnimationOptions.TransitionCrossDissolve, animations: { () -> Void in
self.backgroundImageView.image = image
}, completion: nil)
}
Any ideas? Thanks :)
Change your function signature to this, where you take both the image you want to change and the UIImageView itself.
func animateChangeWithImage(image: UIImage, inImageView: UIImageView)
The problem was with index. I was getting the index as the uses was scrolling through a scroll view, which would constantly keep updating the index variable and not give it enough time to actually animate.
Putting changeBackgroundAtIndex inside scrollViewDidEndDecelerating instead of scrollViewDidScroll fixed it.
It could be that the actual image itself is not animated when changed given that it is not a UIView.
This would seem to do what you want: How to animate the change of image in an UIImageView?
Related
I have created an animated label with UIView that is glitches when switching between View Controllers within a Tab Bar.
I've attached a GIF of what's occurring and a picture of my storyboard layout below, respectively.
Glitching Label
Storyboard Setup
Caveat: I realize that my code is not-so-elegant, to put it nicely, but does anyone know why this is happening or how I can
A) stop this from happening?
and/or
B) fix it?
Is it my code (probably), a bug within the simulator (not likely), or am I approaching the animation incorrectly?
I'm using this
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
self.updateGSLabel(textArray: ["Game Mode", "Play"], index : 0)
}
func updateGSLabel(textArray : [String], index : Int){
UIView.animate(withDuration: 1.25, animations: {
self.gameModeLabel.alpha = 0.0
}) { (success) in
self.gameModeLabel.text = textArray[index]
UIView.animate(withDuration: 1.25, animations: {
self.gameModeLabel.alpha = 1.0
}) { (success) in
var newIndex = index
if(newIndex == textArray.count - 1) {
newIndex = -1
}
self.updateGSLabel(textArray: textArray, index : newIndex + 1)
}
}
}
EDIT
This may have something to do with closure parameters, but I don't know. If someone could elaborate better that'd be appreciated.
I tried to link to a page about it, but apparently I need at least 10 rep to post more than 2 links, which I think I've already done so I don't know how that's possible.
I have a class with...
override func viewDidLayoutSubviews()
{
showDefaultCustomProcessing()
}
On one view, the user can tap and change an image...
#IBAction func changeDoorTap(t:UITapGestureRecognizer)
{
thing.image = UIImage(named:...new image...)
}
It turns out, whenever you change .image, it does a relayout of the screen.
(I did not know that, but it makes sense.)
So unfortunately viewDidLayoutSubviews runs and the CustomProcessing is returned to the default state. That's no good.
As a workaround I did this ........
var requestCustomLayoutAfterViewDidLayoutSubviews:Bool = false
#IBAction func changeDoorTap(t:UITapGestureRecognizer)
{
saveTemporaryCustomProcessing()
thing.image = UIImage(named:...new image...)
requestCustomLayoutAfterViewDidLayoutSubviews = true;
}
override func viewDidLayoutSubviews()
{
performDefaultCustomProcessing()
if (requestCustomLayoutAfterViewDidLayoutSubviews)
{
recoverTemporaryCustomProcessing()
}
requestCustomLayoutAfterViewDidLayoutSubviews = false;
}
Using such a flag is a poor pattern.
1) Is there a way to change a .image but force no relayout?
(Perhaps, by manipulating it at the layer level, or something?)
2) Can you perhaps "turn off" re-lay-out-ing and then turn it on again?
Is there a way to change a .image but force no relayout
Sure: give the UIImageView a width constraint and a height constraint. Without these, it adopts the size of its image. With them, it just obeys the constraints.
I have a function in my UIView subclass. This function adjust the layout of the view.
For example
func addItemAtIndex(index: Int){
// layout the view
item[index].frame.size = view.frame.size
}
I can call myView.addItem(5) as normal.
Since this function adjust the view's layout, I can also call this function inside UIView's animation block. And it will layout the view with animation.
Like this,
UIView.animateWithDuration(0.3){
myView.addItem(5)
}
However, I want to do the different behaviour when it's animated. Is it possible to check if the function get called inside UIView's animation block ?
Like this,
func addItemAtIndex(index: Int){
if insideUIViewAnimationBlock{
// layout with animation
}else{
// layout
}
}
I'm not looking for addItemAtIndex(index: Int, animated: Bool) approach.
CALayer associated with the UIView is disabled implicit animation by default. It will be reenable inside an animation block. You can know whether inside or outside animation block to use that.
print("Outside animation block: \(self.view.actionForLayer(self.view.layer, forKey: "position"))")
UIView.animateWithDuration(5) { () -> Void in
print("Inside animation block: \(self.view.actionForLayer(self.view.layer, forKey: "position"))")
}
For more information:
https://www.objc.io/issues/12-animations/view-layer-synergy/
I'm using YALFoldingTabBar for a project https://github.com/Yalantis/FoldingTabBar.iOS
I want to change its behavior a bit, by showing the current ViewController as the centerButtonImage, instead of a plus/cross sign.
I've tried to override didSelectItem like this:
override func tabBar(tabBar: UITabBar, didSelectItem item: UITabBarItem!) {
switch(self.selectedIndex)
{
case 0:
self.centerButtonImage = UIImage(named: "whatever")
default:
//Change image
}
switch (self.selectedViewController)
{
case 0 as GameViewController:
//Change image
default:
//Change image
}
}
The code below is in my CustomTabBar, subclassing YALFoldingTabBarController.
But it doesn't seem to work that way. I've also tried to manipulate the tabBar in the specific UIViewControllers's viewDidLoad or viewDidAppear but it seems like the tabBar is set only.
Is this possible, and if so, what am I doing wrong?
EDIT: It seems like the method is not called, when changing viewController.
I never got didSelectItem to work, and neither the UITabBarControllerDelegate´s equivalent. However, YALFoldingTabBar have delegate methods tabBarViewWillExpand and tabBarViewWillCollapse and I used these along with tabBarView.layoutSubviews() to solve my problem. It's not very beautiful, but works as intended.
func tabBarViewWillExpand(tabBarView: YALFoldingTabBar!) {
self.centerButtonImage = UIImage(named: "plus_icon")
//Helper method from other thread, link below
performBlock({ () -> Void in
tabBarView.layoutSubviews()
}, afterDelay: 0.25)
}
How do you trigger a block after a delay, like -performSelector:withObject:afterDelay:?
I want to rotate a UIImage, I have managed to do so with the code below, however when I press the rotate button again, the image does not rotate anymore, could someone please explain why?
#IBAction func rotate(sender: UIButton) {
UIView.animateWithDuration(0.2, animations: {
self.shape.transform = CGAffineTransformMakeRotation(CGFloat(M_PI_4) * 2)
})
}
You are changing your shape image view's transform to a new, fixed value. If you tap on it again, the transform already has that value. You set the transform to the same value again, which doesn't change anything.
You need to define an instance variable to keep track of the rotation.
var rotation: CGFloat = 0
#IBAction func rotate(sender: UIButton)
{
UIView.animateWithDuration(0.2, animations:
{
self.rotation += CGFloat(M_PI_4) * 2 //changed based on Daniel Storm's comment
self.shape.transform = CGAffineTransformMakeRotation(rotation)
})
}
That way, each time your tap the button you'll change the rotation variable from it's previous value to a new value and rotate to that new angle.