I'm new to CAShapeLayers so forgive me if this is an obvious fix. Im trying to move a line in sync with a UIScrollView when the view is moving up but keep the line still when the scroll view is moving down. I am using a delegate to detect when the scroll view is moving and this is my code for changing between the visible line.
func scrollViewDidScroll(_ scrollView: UIScrollView){
if(mainScrollView.contentOffset.y < CGFloat(0.0)){
topMainContentViewLine!.isHidden = false
largeRocketImageView!.isHidden = true
topViewLine!.isHidden = true
}else if(mainScrollView.contentOffset.y == CGFloat(0)){
topMainContentViewLine!.isHidden = false
topViewLine!.isHidden = false
}else{
largeRocketImageView!.isHidden = false
topMainContentViewLine!.isHidden = true
topViewLine!.isHidden = false
}
}
When I run my app I can easily see that the two lines are fading in and out instead of appearing and disappearing instantly. For comparison the image has no animation. Any ideas on how to get rid of the animation? Thanks again!
From the documentation:
https://developer.apple.com/documentation/quartzcore/calayer/1410838-ishidden
isHidden
A Boolean indicating whether the layer is displayed. Animatable.
See that word "animatable"? That is a rather tight shorthand; it means that when you set this property on a layer that is not the underlying backing layer of a UIView (e.g. any standalone sublayer), animation is the default. That's called implicit layer animation.
If you do not want animation of an animatable property under those circumstances, you can turn animation off by calling
CATransaction.setDisableActions(true)
You can do that either for a specific explicit transaction block (i.e. everything between matching begin() and commit() commands), or for the entire implicit transaction in which all your code runs.
More commonly, if you have a layer for which you always or mostly don't want animation, you'd host it as the underlying layer of a custom UIView. In that case, setting an animatable property would not animate, and if you needed animation, you would use an explicit CAAnimation (or UIView animation) to get it.
For comparison the image has no animation
Because largeRocketImageView is not, itself, a layer; it's a view.
Related
So I have a button on my storyboard. When that button is pressed, I want to move a certain label down a little bit and also add a new text field to the screen. Here is the simplified code:
UIView.animateWithDuration(0.5, animations: { () -> Void in
self.addIngredientLabel.center.y += self.addIngredientLabel.bounds.height
}) { (_) -> Void in
let newIngredientTextField = UITextField(frame: CGRectMake(100, 110, 100, 100))
newIngredientTextField.center.y -= 50
newIngredientTextField.placeholder = "place text here"
newIngredientTextField.contentHorizontalAlignment = .Left
self.view.addSubview(newIngredientTextField)
}
The problem though, is that after the animation is completed, the label jumps back to its original location. It is like the animation is being pre-maturely terminated. The textField gets added and the label moves but it does not stay moved.
I have tried adding the text field at the end of the function instead of in a completion block. No kind of reordering seems to be helping.
I feel like I am not understanding something fundamental to animations. If anyone has any advice I would greatly appreciate it.
Since you're using the storyboard, you are most likely also using auto-layout. The positions of your controls are determined in your visual design (storyboard) and will adjust automatically to orientation changes and adapt to various device screen sizes. This is good but the flip size is that you cannot directly change the positions of your control using their x,y coordinates.
Instead, you can change values in the constraints you defined in the storyboard. for example, if you button has a "Top" distancs constraint with a control above it (or the edge of the view), you can change the constraint's "constant" attribute to indirectly change the position of the control.
In order to manipulate the contraints in your code, you can create IBOutlets for them just as you would do with any control.
How I create UILabel with auto scroll from left to right?
like this http://www.youtube.com/watch?v=moI3ROPBm4Y
Thanks!
I use this code for this (From YouTube):
-(void)time:(NSTimer *)theTimer{
textLabel.center = CGPointMake(textLabel.center.x - 2.2, textLabel.center.y);
if (textLabel.center.x < -(textLabel.bounds.size.width/1.5)){
textLabel.center = CGPointMake(320 + (textLabel.bounds.size.width/1/5), textLabel.center.y);
}
how I to change the move label from left to right?
You can have a looping selector moving the viewarea of the frame slowly sideways. That should do the trick, without blocking the UI.
You would have to create a subclass of UILabel that overrides the drawRect. Then on a timer you would redraw the text with an offset, most easily with a ever increasing translation transform on the graphics context.
You also have to be careful to schedule the timer on the common modes because otherwise the scrolling of the label would stop as soon as the user starts scrolling any scroll view.
I have a subclass of UICollectionViewLayout which places cells in a circle. The layout returns YES for the call shouldInvalidateLayoutForBoundsChange:. On rotation, the cell in the initial position fades out and the cell in the final position fades in.
By adding the following code to my layout I can disable the fades and the
circle of items appears to to simply rotate with the orientation change:
- (UICollectionViewLayoutAttributes *)initialLayoutAttributesForAppearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
return nil;
}
- (UICollectionViewLayoutAttributes *)finalLayoutAttributesForDisappearingItemAtIndexPath:(NSIndexPath *)itemIndexPath {
return [self layoutAttributesForItemAtIndexPath:itemIndexPath];
}
Why do the methods get called on a bounds change because documentation doesn't seem to suggest they do? Documentation seems to state they get called related to insertion and removal of items from the collection view.
Is there a better way to disable the cross fade during rotation?
Notes:
The initialLayoutAttributesForAppearingItemAtIndexPath: documentation
states that by default the method returns nil but calls to super returned
non-nil values.
I set symbolic breakspoints on the UICollectionView methods
deleteItemsAtIndexPaths:, moveItemAtIndexPath:toIndexPath: and
insertItemsAtIndexPaths: and none of them are hit during rotation.
The UICollectionViewLayout.h file states
// This set of methods is called when the collection view undergoes an
animated transition such as a batch update block or an animated
bounds change.
// For each element on screen before the invalidation,
finalLayoutAttributesForDisappearingXXX will be called and an
animation setup from what is on screen to those final attributes.
// For each element on screen after the invalidation,
initialLayoutAttributesForAppearingXXX will be called an an
animation setup from those initial attributes to what ends up on
screen.
which clearly says they are called on bounds changes. Rather than removal/insertion, "old state" and "new state" seems more accurate.
I have a CAGradientLayer that I want to hide sometimes. The .hidden is a read-only property. The CAGRadientLayer is added as a sublayer to my view. I put in NSLog statements to see if my conditions are being hit and they are the way I want. However, sometimes the .opacity = 0.0 does hide the layer, sometimes it doesn't. Has anyone else had consistency problems with this? Is there a better way to hide the layer? Thanks.
Why do you say that the hidden property is readonly? It's writable:
/* When true the layer and its sublayers are not displayed. Defaults to
* false. Animatable. */
#property(getter=isHidden) BOOL hidden;
I have a CALayer which animates (moves) off-screen on the y axis.
After it's off-screen i'm doing a [myLayer removeFromSuperLayer] so its gone from the view and does not go back to start position.
While the layer is still in the view it can be paused and resumed by pushing a toggle button and this works all fine.
The only thing is that after the [myLayer removeFromSuperLayer] has run my app crashes.
This is caused by the fact the button is trying to pause or resume the layer which doesn't exist anymore.
How can i check if the layer is removed or still in the view?
I thought something like this for the pause part of my toggle button:
if (self.myLayer == nil)
{
// here i want to add the layer again
[self.view.layer addSublayer:myLayer];
// immediately pause it
[self pauseLayer:myLayer];
}
else
{
// just pause no need to create the layer again because it's still there
[self pauseLayer:myLayer];
}
As you might suspect the self.myLayer == nil is not the way to do it, but what is?
Thanks in advance.
Removing a layer from its superlayer will not cause the layer to become nil, which is why your self.myLayer == nil check is not working. However, you could easily set the field to nil when you remove it, like:
[self.myLayer removeFromSuperLayer];
self.myLayer = nil;
Of course, if you need to add it again after that then you'd need to either reassign the layer to self.myLayer (if you have a reference to it that you're keeping somewhere else) or create a new instance of the layer from scratch.