CALayer setNeedsDisplay can not work? - objective-c

My class is a CALayer subclass, and I have added a observer through NSNotificationCenter, when catching the message, it will run function like this:
- (void)setCurrentAutoValue:(NSNotification *) obj
{
[self setNeedsDiaplay];
}
but this does not work, can anyone help?

Apparently, it is necessary to call setNeedsDisplay on the main thread. You can do this by using the performSelectorOnMainThread method. For example in your case:
[self performSelectorOnMainThread:#selector(setNeedsDisplay) withObject:0 waitUntilDone:NO];

I had the same issue updating my CALayers through another thread. If i called any of the following lines directly in the same thread it worked perfectly:
[self setNeedsDisplay:YES];
[self needsDisplay];
[self.circleLayer needsDisplay];
[self.circleLayer setNeedsDisplay];
But when calling these through another thread, there was no change. I played around a bit and ended up using
[self display];
which was the only thing that forced the CALayers to update. Self is an NSOpenGLView which contains several layers and sublayers. Maybe this can help. Every CALayer has the same method available. Maybe you should call display.

My problem was that i could only update a CALayer with an animation, not by setting it's content (a CGPath to the property "path"). What seems to be the only solution was removing all animations of the layer first and then set the new path:
[_myLayer removeAllAnimations];
UIBezierPath *newPath = ...
_myLayer.path = newPath.CGPath;

Note to self: Or the frame of the CALayer was never set. Whenever there's a problem with a CALayer, first makes sure its frame != NSZeroRect.

Related

NSView drawRect method not working?

Right now I have two windows. One of them has an NSView, which acts as a background color and the other one has a colorwell that changes a color variable in a shared instance.
My program sort of works. I can open my colorwell window and select a color, but the background color only updates if I manually resize the window.
I tried to get past this issue by having a thread in the background looping:
[self setNeedsDisplay:YES];
[self updateLayer];
[self display];
I'm not completely sure that those 3 lines are necessary, but I am sure that they are re-calling my drawRect method.
I even threw an NSLog into the drawRect method to test. I saw in the console that it was getting called over-and-over again.
Here is my drawRect method:
- (void)drawRect:(NSRect)dirtyRect{
[theDATA.main_frame_background_color setFill];
[NSBezierPath fillRect:dirtyRect];
}
theDATA.main_frame_background_color is an NSColor from a shared instance. I am positive that the value is changing because my NSView updates when I resize the window.
I'm completely clueless on why this is not working. Hope you can help.
First, never call a display method directly. Mark dirty area with setNeedsDisplay: and the view will be redrawn after a while. If your view is layer-backed, be aware of layerContentsRedrawPolicy property, since it's default value is NSViewLayerContentsRedrawNever. There's no way to automatically determine which policy fit your needs best, so this property should be set manually.
I solved my problem thanks to #Sega-Zero's suggestion of using layerContentsRedrawPolicy and through a bit of research I found out that I couldn't directly call setNeedsDisplay in a thread, so I used performSelectorOnMainThread.
My Thread class:
- (void)updateLOOP{
while (true){
[self performSelectorOnMainThread: #selector(refreshClock)
withObject: nil
waitUntilDone: NO];
[NSThread sleepForTimeInterval:.1];
}
}
}
External update method:
- (void) refreshClock{
[self setNeedsDisplay:YES];
}
Now everything works as it should. The drawRect method is getting called without having to use [self display].

Objective-c: recursive call of viewDidLoad after addSubview

I am working in Xcode 4.5.2.
I have the following problem:
When i make projects from non-empty projects - that is the views are initialised from the storyboard, sometimes when i add the code
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIButton *b = [UIButton buttonWithType:UIButtonTypeRoundedRect];
b.frame = CGRectMake(20, 20, 100, 100);
[self.view addSubview:b];
to the viewDidLoad method it is recursively called again after the addSubview method call and sometimes (bless the sky) it doesn't.
Can anyone tell me what's the problem and how to solve it?
My gratitude.
Edit: As #WDUK correctly states, the following answer is wrong. It would explain recursive in loadView, not viewDidLoad.
Old, wrong answer:
Recursive calls occur when after the call to -[super viewDidLoad]
the view property has not been set and you then call self.view.
You probably just forgot to connect the view to the file's owner's
view outlet.
When the device runs out of available memory, it could dealloc the view if it is not currently visible. You should always make sure you handle this behaviour correctly.
I could happen when you for example switch to another view when using a navigation or tabbarcontroller.

Redrawing a view in ios

Is it possible to redraw the whole view.
I need it to complete my language settings. The problem is that the language only changes after the views there drawn again. Like you go out of settings and then go in again, then the language is changed. But the moment you save everything, the language stays the same.
So how should i redraw my views, or by best the whole app, after the language change was found?
In ARC:
- (void)setLanguage:(LanguageType)languageType
{
_language = languageType;
//TODO:your settings
[_theWholeView setNeedsDisplay];
}
If that can not work , there is a very troublesome solution , it can reload the whole view.
You can code like this:
In the view.h , you need creat a delegate.
#protocol WholeViewDelegate
- (void)reloadData;
#end
In the view.m
- (void)setLanguage:(LanguageType)languageType
{
_language = languageType;
//TODO:your settings
[_delegate reloadData];
}
In the controller you need to implement the delegate
In the controller.m
- (void)reloadData
{
if(_wholeView)
{
[_wholeView removeFromSuperview];
}
// in the wholeView init method you should refresh your data
_wholeView = [[WholeView alloc] init];
self.view addSubview:_wholeView
}
Yes. Call [UIView setNeedsDisplay] (reference).
Just call setNeedsDisplay.. It will resolve the problem. setNeedsDisplay actually calls the drawRect function in the UIView class by passing the frame of the view as the rectangular parameter. Hope that helps....

When is it safe to manipulate screen controls through code in iOS/Objective-C

I have a question regarding iOS (or perhaps more accurately Objective-C) and properties. I have a UIView with a UISegmentedControl, by default it has 3 segments. I have a message which accepts a parameter and based on this parameter I may want to remove one of the segments. In UIView A I do this:
MyViewController *myview = [[[MyViewController alloc] initWithNibName:#"MyViewController" nib:nil] autorelease];
[[self navigationController] pushViewController:myview animate:YES];
[myview showItem:item];
In UIView B this happens in showItem:
-(void) showItem:(Item*)item{
if (item.removeSegment){
[segmentControl removeSegmentAtIndex:0 animate:NO];
}
}
I have noticed that the segment only gets removed when I call showItem after I have pushed it on the navigation controller. When I swap those two line, so I first call showItem and then push the view, the UISegmentedControl still has three segments instead of two.
This just feels wrong, it seems like bad practice that my code will break if someone doesn't call two messages in the right order. Is there a better way to do this? I've been looking at some sort of a property lifecyle that I can use, I am very familiar with this from ActionScript 3, but I have been unable to find anything on the subject.
(as an aside: in AS3 I would make a property, in the setter I don't manipulate any screen controls but call InvalideProperties. My overriden methode CommitProperties will be called once the entire object and child controls have been created. In CommitProperties I check if my property value has changed and this is where I would remove the segment.)
A common way of doing something like this is to create an Item *item property in MyViewController and set that when myview is created. So, your code becomes:
MyViewController *myview = [[[MyViewController alloc] initWithNibName:#"MyViewController" nib:nil] autorelease];
myview.item = item;
[[self navigationController] pushViewController:myview animate:YES];
MyViewController would then use that property in its viewWillAppear: method to configure its own segment control.
I think what you are falling prey to is myview->segmentControl doesn't exist until myview.view is referenced because of the lazy load of the view.
-(void) showItem:(Item*)item{
[self view]; // NO OP TO FORCE LOAD!!
if (item.removeSegment){
[segmentControl removeSegmentAtIndex:0 animate:NO];
}
}
Should work for you. Hope this helps!

setNeedsDisplay not working?

I have a problem redrawing a custom view in simple cocoa application. Drawing is based on one parameter that is being changed by a simple NSSlider. However, although i implement -setParameter: and -parameter methods and bind slider's value to that parameter in interface builder i cannot seem to make a custom view to redraw itself.
The code that does redrawing is like this:
- (void)setParameter:(int)newParameter {
parameter = newParamter;
NSLog(#"Updated parameter: %d", parameter);
[self setNeedsDisplay:YES];
}
I DO get the message about setting the new parameter although the view doesn't redraw itself. Any ideas are welcome!
The usual syntax is: [self setNeedsDisplay:YES], although I would assume that that means the same thing. Are you implementing the - (void)drawRect:(NSRect)rect method, or using the drawRect: method of your superclass?
For anyone that has this problem while using an NSOpenGLView subclass, you might be forgetting to use [[self openGLContext] flushBuffer] at the end of drawRect:.
Sometimes reason can be very simple: File's owner has no connection to UIView object. i.e. it's Outlet is not setup properly.
Use IB, ctrl button and drag method :)
In the iOS 6 there isn't such function to call: setNeedsDisplay:YES. I've got the same problem, and came with this solution: https://stackoverflow.com/a/15027374/1280800 .
Hope it will help.