I'm trying to toggle the fill for a Core Plot plot area on and off. When I set up the fill at application launch everything works. Toggling the first time works fine. Subsequent toggling of the fill on or off does not work unless a rotate the display.
-(void)layoutGraphPlotAreaFill{
CPTFill *plotFill=nil;
if (self.graphPlotAreaHasFill) {
plotFill=self.graphPlotAreaFill;
}else{
plotFill=nil;
}
self.graph.plotAreaFrame.plotArea.fill=plotFill;
}
I'm calling the above code from a settings view controller via a delegate method. NSLog statements within the above method (removed) show that the method is being called, that the graphPlotAreaHasFill property is set correctly and the CPTFill is correct. It just isn't updating the plotArea fill. Again, it will only update after rotating the display.
Using Core Plot 1.0.
Any suggestions on how to force the update of the fill area without having to rotate?
Thanks
found the answer here: Calling setNeedsDisplay:YES on layer-hosting view does not redraw the view
turns out because the fill is a CALayer subclass, I can call setNeedsDisplay and it will refresh. Update code is as follows:
-(void)layoutGraphPlotAreaFill{
CPTFill *plotFill=nil;
if (self.graphPlotAreaHasFill) {
plotFill=self.graphPlotAreaFill;
}else{
plotFill=nil;
}
self.graph.plotAreaFrame.plotArea.fill=plotFill;
[self.graph.plotAreaFrame.plotArea setNeedsDisplay];
}
Related
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.
I have a strange problem: in my table view I want to update a single cell when its UISlider gets modified (value changed control event). At the moment the value of the slider changes, I want to animate a button into the cell (sliding in from the right). I set the constant of the button's constraint to -50 to make sure it's not visible by default, and when the slider's value gets changed, a method gets called which updates the table view, so cellForRowAtIndexPath gets called again for all cells in my table. Simplified it looks something like this:
func valueChanged(sender:UISlider) {
// Reload table view
self.myTableView.reloadData()
// Let table view know the value of the slider got modified
self.didChangeValueOfSlider = true
}
And in cellForRowAtIndexPath I'm keeping track of which cell's slider got changed by using a custom selectedCellIndexPath variable. When the table view gets to the cell that got modified: it runs the following code:
// Check if cell is selected
if indexPath == self.selectedCellIndexPath {
// Check if value of slider was changed
if self.didChangeValueOfSlider == true {
// Value was changed: set constraint back to default
saveButtonTrailingConstraint.constant = CGFloat(15)
self.view.setNeedsUpdateConstraints()
self.view.updateConstraintsIfNeeded()
// Reset slider update status
self.didChangeValueOfSlider = false
}
}
Calling those setNeedsUpdateConstraints() and updateConstraintsIfNeeded() might be overkill or unnecessary, but please note this is my 15th attempt or so to actually get the view to display the updated layout. I used breakpoints to confirm the constant actually changes, after the code above is finished running and everything works perfectly fine. The only thing I can't get working is the updating part. I've tried UIView.animateWithDuration to animate the change, and I've tried methods like layoutIfNeeded() and beginUpdates() and endUpdates of the table view: nothing works. What could be the reason the layout doesn't get updated? And what am I supposed to be calling the layout and update methods on? I've been calling them on self.view but I'm not even sure if you're supposed to be calling it on the view if you're trying to update the layout of a table view cell. Any help would be appreciated.
I figured it out. The problem turned out to be the constraint itself. as I was accessing the wrong one. I defined it as saveButtonTrailingConstraint but I was actually modifying was the saveButton's width constraint (which apparently can't be animated). Instead of accessing saveButton.constraints[indexOfTrailingConstraint] I should have defined saveButton.superview!.constraints[indexOfTrailingConstraint] as the constraint belongs to the superview and not to the button itself. I guess the best thing to take away from this clumsy mistake is to always double-check if you're modifying the correct constraint if you're doing it programmatically, because it doesn't always show on the first eye.
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 was just experimenting with layer property of my UIButton by setting a outlet n the code for a button created in xib.
NSLog(#"d button layer value%#",dButton.layer.backgroundColor);
But the output is coming as:
d button layer value(null)
My question is: can't we display the layer property value of UIButton. We all know that some default value would have been set for the button.
According to the documentation for CALayer:
The default is nil.
unless you explicitly change it.
In the documentation for UIView, it says this:
The default value is nil, which results in a transparent background color.
This excerpt describes the view.backgroundColor property, so I assume that the view.layer.backgroundColor property is the same.
Hope this helps!
Phew !! I finally got the answer.
Actually I was trying to display the structure value using %# in NSlog statement.
dButton.layer.backgroundColor is a fundamental data type used internally by Quartz to represent colors.
So, way to display them is to make your own class which can parse the value.
Like, for example there are class functions for CGRect, CGPoint etc
like for displaying CGRect we use this code
NSLog(#"d button layer value%#",NSStringFromCGRect(dButton.layer.bounds));
but there arent any function defined for CGColorRef.
Ok, as per jrturton said,for displaying the above value we can use
NSLog(#"d button layer value%#",[UIColor colorWithCGColor:button.layer.backgroundColor)]
I hope everyone got it now !!
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.