iOS Set Property After Delay - objective-c

Is there a way to set a property after a delay? Such as:
// Compiler hates this
[self performSelector:#selector(imageView.image) withObject:[UIImage imageNamed:newImage] afterDelay:0.5];
Or do I have to create a new method to be a called to put the setting of the property in? The above seems so simple.

Yes the default name of the setter for a property named image will be setImage.
[imageView performSelector:#selector(setImage:) withObject:[UIImage imageNamed:newImage] afterDelay:0.5];
When you do not set the setter method it will generate a method prefixed with set then camel cased and it will accept a parameter of its type.
Ex.
#property(assign) id object;
//becomes
-(id)object {...} // getter
-(void)setObject:(id)inObject {...} //setter

Alternatively, use dispatch_after. If you type dispatch_after and ask for code completion, you get three lines of boilerplate code and a place to put your actual code in a block. In that block, imageView.image= is legal. Don't forget to change the default delay time from 2 seconds (kind of a long delay!).

Related

Initializing a constant that's value takes a completionBlock argument in its initializer

I have a property, that in Objective-C I created like this:
self.myProperty = [[MyClass alloc] initWithCompletionBlock:^(MyClass *object) {
dispatch_async(dispatch_get_main_queue(), ^{
[self doSomethingAfterInitialization];
});
}];
And it worked great. Initialization of the MyClass object could create an indeterminate amount of time, so I passed a completionHandler in to it. When it finished, doSomethingAfterInitalization: would handle business.
Now in Swift, I'm trying to create the same object and assign it to a property, with problems.
The property never will change, so it makes sense to me to create it as a Swift constant.
So I'm trying it like this:
let myProperty = MyClass(completionBlock:{ (MyClass) -> (Void) in dispatch_async(dispatch_get_main_queue(), doSomethingAfterInitialization())})
To me that seemed like a direct translation... but the Swift compiler tells me that's not correct, via the error
Use of instance member 'doSomethingAfterInitialization' on type 'MyViewController'; did you mean to use a value of type 'MyViewController' instead?
Well that didn't help much. So instead I tried changing the call to the doSomethingAfterInitialization function to self. doSomethingAfterInitialization(), in which case I see
Value of type '(NSObject) -> () -> TodayWidgetTableViewController' has no member 'doSomethingAfterInitialization'
Any idea how I can fix this? Obviously my initializer is a little weird in the first place, so I'm wondering if this is something that doesn't really translate at all to Swift.

Do I need to Block_copy a block that I pass to NSOperation's setCompletionBlock:?

I have a NSOperation's subclass and I want to do something like this:
#interface MyOperation : NSOperation...
MyOperation *myOperation = [[MyOperation alloc] init];
[myOperation setCompletionBlock = [^{
[myOperation doSomething]; // Do something with myOperation and other classes captured from original environment
[myOperation setCompletionBlock:nil]; // Manually setting to nil avoid retain cycles;
} copy]; <- is this "copy" needed here?
And so my question for this code is the title question:
Do I need to copy (Block_copy()) the block I pass to setCompletionBlock: or NSOperation will do it for me?
From the -[NSOperation setCompletionBlock:] reference:
The block to be executed when the operation finishes. This method creates a copy of the specified block. The block itself should take no parameters and have no return value.
So no, you don't need to copy the block as NSOperation already does this for you.
Any API that explicitly takes a block type parameter must already take care of whether to copy it or not.
Only when you pass a block to an API that takes a generic object pointer type argument do you ever need to worry about whether to copy it.

Call a method every time a parameter is set on Objective-C (Cocoa)

I currently have a class with 15 properties (and growing), and I'm finding myself having to call an update method every time one of those properties change.
Currently, I'm overriding every setter with a code like this:
-(void)setParameterName:(NSUInteger)newValue {
if (_param == newValue)
return;
_param = newValue;
[self redraw];
}
The method [self redraw]; being the key here.
Is there a better way to do it? Should I be using keyValue observers (the method observeValue:forKeyPath:ofObject:change:context:)?
Notes:
All properties (so far) are assign (mostly enum, NSUInteger, CGFloat and BOOL);
All those properties are set using bindings (method bind:toObject:withKeyPath:options:). Except when loading from the filesystem (which is not important, as I already call the drawing methods on every object after the loading is done);
The value changes are only for the current object. I do not need to be told when changes occur on other objects;
I have other properties that I don't need to watch the changes on it (because it will have no effect on my output and drawing the output is kinda time-consuming).
Thanks!
Since these properties are updated using bindings, which invoke -setValue:forKey:, you can override that method instead of writing custom setters:
+ (NSArray *) keysAffectingDrawing {
static NSArray *singleton;
if (!singleton)
singleton = [NSArray arrayWithObjects:
#"property1",
#"property2",
#"property3",
nil];
return singleton;
}
- (void) setValue:(id) value forKey:(NSString *) key {
[super setValue:value forKey:key];
if ([[CustomClass keysAffectingDrawing] containsObject:key]) [self redraw];
}
(I was first inclined recommend key-value observing but agree it's not the best solution here. I think the reason is in part that there's only one object, and in part because the design doesn't follow MVC. Usually in MVC an object that draws itself isn't the one with all the properties.)
(Added: Ahh, I see. The model is responsible for rendering the properties to a bitmap, and that's what -redraw does. That's fine MVC. To make it clearer, I recommend changing the name of the method from -redraw to something like -updateImage or -renderImage, since it doesn't actually do any drawing.)
You could use the Key-Value Observing to avoid repeating in all properties setter the method call, however i think that calling the method directly in the setter is not the wrong way to do it, and could even be faster ...

Correct way of setting a BOOL property

I have a BOOL property that I want to set in my class initializer.
#property (assign, nonatomic) BOOL isEditMode;
- (id)init
{
. . .
[self setValue:NO forKey:isEditMode];
return self;
}
The compiler gives me an "Incompatible integer to pointer conversion" warning. What am i doing wrong here?
The Key-Value Coding method setValue:forKey: only accepts objects as arguments. To set a BOOL, you need to wrap the number in a value object with [NSNumber numberWithBool:NO]. But there's little reason to do that. Key-Value Coding is a roundabout way to accomplish this. Either do self.isEditMode = NO or just isEditMode = NO. The latter is preferable in an init method (because setters can run arbitrary code that might not be desirable before an object is fully set up).
But to elaborate on the first point: The reason Key-Value Coding works this way is because the type system can't represent an argument that's sometimes an object and at other times a primitive value. So KVC always deals with objects and just autoboxes primitive values as necessary. Similarly, if you do [yourObject valueForKey:#"isEditMode"], you'll get back an NSNumber object wrapping the real value.
The correct syntax to set a property is just
self.isEditMode = NO;
If you want to use -setValue:forKey: you'd have to write it as
[self setValue:[NSNumber numberWithBOOL:NO] forKey:#"isEditMode"];
However, there's absolutely no reason to do this in your situation.
That said, since you're in an init method, I would strongly recommend avoiding any property access whatsoever and instead using the ivar directly, as in
isEditMode = NO;
This avoids the possibility of an overridden setter being called (either in this class or a subclass) that makes the assumption that the object has already completed initialization. For this same reason you also want to avoid property access inside of -dealloc.
You can just assign the value directly:
isEditMode = NO;
I think you mean:
self.isEditMode = NO;
If your code does indeed compile (I'm pretty new to Objective-C so I don't know) setValue probably takes a pointer to a string (#"isEditMode", e.g.) and not some other type (isEditMode, e.g.).

How could I pass the float number by value in Objective-C?

In the following code, I passed the "1.3f" to printFloat, but it was the wrong value (in this case, -2.000000) after was "f" received. And there is a warning that "AppDelegate may not respond to -printFloat:"
Where did I get it wrong?
- (void)applicationDidFinishLaunching:(UIApplication *)application{
// Override point for customization after application launch
[window makeKeyAndVisible];
[self printFloat:1.3f];
}
- (void)printFloat:(float)f {
NSLog(#"%f",f);
}
You can easily resolve both your problems by adding the method prototype into the header file (<sameName>.h):
- (void)printFloat:(float)f;
If you include the correct definition in the class' header file, and you still cannot pass floats by reference, make sure you're not overriding an existing method of a parent class...
For example, I had this problem because my class was derived from NSMutableData (which I did not know then), and I added a method,
- (void) initWithLength:(float)length;
to my class. NSMutableData already defines this method, and it uses an integer.
My method then produced garbage: the float value was passed as 0x0 no matter what I sent it...