What is the -(CGRect *)frame method in iOS? - ios

Codes:
if (!CGRectContainsPoint([[viewArray objectAtIndex:0] frame], CGPointMake(newX, newY)) )
{
....
}
Question
I wanna to get a "frame" value of a UIView in a view array, so i used this:
((UIView *)[viewArray objectAtIndex:0]).frame
I utilize (UIView *) to compulsively convert an id object to UIView, otherwise it will trigger error like "Property 'frame' not found on object of type 'id'". But the method below seems successfully escape from this error:
[[viewArray objectAtIndex:0] frame]
So [id frame] can Automatically detect the id's real type and then make it to call the method?
I really wanna to know methods like
[id frame]----id's real type is UIView
[id view ]----id's real type is UIViewController
Are these methods getter methods or setter methods? I cannot tell the diffrence and the apple document show them like :
#property(nonatomic) CGRect frame
Thx a lot for your help!

The method called in the end is -(CGRect)frame;. Using the dot syntax is just a syntactic sugar the objective-c compiler provides. For this sugar to work, the compiler needs to know the type of the object it uses.
On the other end, objective-c uses message passing for calling methods. Which means that when you use the syntax [id message], it will try to execute the selector #selector(message) on id. id being a special type denoting any object in the runtime, it accepts every selector and will throw an unknown selector exception at runtime if the object cannot execute it.

By defining a property like: #property(nonatomic) CGRect frame the property works as both a getter and setter.
For example, to modify the width of a frame, you might write:
CGRect frame = myView.frame;
frame.size.width = 100;
myView.frame = frame;
For a good description of dot notation, see this fine answer.

Related

How to SuperClass object cast to SubClass [objective -c]

I want to add polyLine to mapView , the baidu mapView polyline superclass is BMKPolyLine, it have a class method:
+ (BMKPolyline *)polylineWithPoints:(BMKMapPoint *)points count:(NSUInteger)count;
I creat a subclass BKMyPolyLine, i add a #property colorString
when I get points and used superclass method :
BKMyPolyLine *myLine = [BKMyPolyLine polylineWithPoints:points count:points.count];
myLine.colorString = colorString;
and crash message:
[BMKPolyline setColorString:]: unrecognized selector sent to instance 0x138266fe0'
This isn't "casting" (casting merely informs the compiler; it does not alter actual types)--but you have done something that should return your subclass. It sounds like BMKPolyline's +polylineWithPoints:count: method returns a BMKPolyline, even when it's called on a subclass. Instead you need to override what's known as the "designated initializer", which should be marked in the documentation. But from what I see online, it isn't documented--you probably (annoyingly) need to look into the source of BMKPolyline to know how to proceed.

CALayer: Where does the implicit animation comes from?

I'm new to Core Animation. While learning about implicit animations, I came across a question.
I put a layer on the screen and made it color change to a random value when pressing a button, which triggers an implicit animation.
#define RANDOM_0_1 (arc4random() / (CGFloat)UINT_MAX)
- (IBAction)changeColor:(id)sender {
CGFloat r = RANDOM_0_1;
CGFloat g = RANDOM_0_1;
CGFloat b = RANDOM_0_1;
self.colorLayer.backgroundColor = [UIColor colorWithRed:r green:g blue:b alpha:1.0f].CGColor;
}
It works pretty well. Then I use
NSLog(#"%#", [self.colorLayer actionForKey:#"backgroundColor"]);
to get the animation object passed implicitly to the layer. I got
<CABasicAnimation: 0x7f8ae1d21970>.
Referring to the document, I learned that there are four way for a layer to get an action. 'Delegate, actions dictionary, style dictionary and +[CALayer defaultActionForKey:]' Then I stated wonder which step does the animation object really comes from. So I wrote this to check
NSLog(#"%# %# %#", self.colorLayer.delegate, self.colorLayer.actions, self.colorLayer.style);
It gave me three (null)
(null) (null) (null)
As the document said, these values should be set to nil by default.
So it must be +[CALayer defaultActionForKey:] that gives me the animation object. However, when I call
NSLog(#"%#", [self.colorLayer actionForKey:#"backgroundColor"]);
it still gave me a (null).
That's quite strange I thought. I started wonder if the 'key' passed into has been somehow changed by the internal implementation. So I referred to this post to print the argument passed to the method as well as the return value.
static id<CAAction> (*__originalCALayerDefaultActionForKey)( CALayer *, SEL, NSString *) ;
static id<CAAction> CALayerDefaultActionForKey( CALayer * self, SEL _cmd, NSString * event )
{
id res = (*__originalCALayerDefaultActionForKey)( self, _cmd, event );
NSLog(#"%#<%p> %# %#\n", [ self class ], self, event, res ) ;
return res;
}
I got the result like this
CALayer<0x106da5ef0> position (null)
CALayer<0x106da5ef0> bounds (null)
CALayer<0x106da5ef0> backgroundColor (null)
the 'key' passed into was exactly the property name, but it always returns null.
So, could anyone explain where does the animation object comes on earth, or provide me some technique to find out the answer?
Thanks. :)
The CALayer's SDK default animations are implemented in the .m, which you cannot get it in normal way.
And the SDK offers optional ways for user to use customised animation:
delegate:
You can use a delegate object to provide the layer’s contents, handle the layout of any sublayers, and provide custom actions in response to layer-related changes. The object you assign to this property should implement one or more of the methods of the CALayerDelegate informal protocol. ...
actions:
The default value of this property is nil. You can use this dictionary to store custom actions for your layer. The contents of this dictionary searched as part of the standard implementation of the actionForKey: method.
they're just for animation customisation.
e.g. you can create a custom CALayer subclass (say CustomLayer) which override the -actionForKey:
- (id<CAAction>)actionForKey:(NSString *)event
{
if ([event isEqualToString:#"strokeStart"] || [event isEqualToString:#"strokeEnd"]) {
CABasicAnimation * strokAnimation = [CABasicAnimation animationWithKeyPath:event];
strokAnimation.removedOnCompletion = NO;
strokAnimation.fillMode = kCAFillModeForwards;
strokAnimation.duration = .3f;
strokAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseInEaseOut];
strokAnimation.fromValue = [self.presentationLayer valueForKey:event];
return strokAnimation;
}
return [super actionForKey:event];
}
in this way, whenever you update strokStart & strokend for your CustomLayer instance, it'll invoke the strokAnimation you offered instead of the default animation that SDK implemented.
But style, it's for setting the animatable properties' value w/o animation (cause by default, whenever you update CALayer instance's animatable properties' value, it'll invoke default animation if you don't offer custom one):
An optional dictionary used to store property values that aren't explicitly defined by the layer.
If the style dictionary does not define a value for an attribute, the receiver’s defaultValueForKey: method is called. The default value of this property is nil.
e.g. like strokeStart & strokeEnd, their default values are 0.f & 1.f, you can modify style to use customized default value, like:
aLayer.style = #{#"strokeStart" : #.2f,
#"strokeEnd" : #.6f};
in this way, the layer's animation won't be invoked, like in the initialisation step, especially when you've offered a complex animation for the key, it'll helps a lot.
For +defaultActionForKey:, it's more like user defined default actions, as the doc said:
Returns a suitable action object for the given key or nil of no action object was associated with that key.
Classes that want to provide default actions can override this method and use it to return those actions.
You can override this method like -actionForKey:, below is the order to check whether the action exists when you modified the related property:
Check whether the delegate exists & delegate method -actionForLayer:forKey: is implemented;
Check whether the action of key was offered in -actionForKey:;
Check whether the action of key exists in actions while invoking [super actionForKey:event] after step 2;
Follows step 3 if no action of key exists in actions, check whether the action of key exists in +defaultActionForKey:.
Use SDK implemented default actions, which we cannot get (or might have unknown event name), as far as I know.
Generally, +defaultActionForKey: is like a global customised setting in class level for your CALayer subclass, while actions, -actionForLayer:forKey: & -actionForKey: to handle special layers.

Calling a instance method while using childNodeWithName

Is it possible to call an instance method without using an instance variable or #property?
Here is how I create an instance of a class. Within the method, I try to call on the class's instance movement method to force the instance to move:
-(void)createCharacterNPC
{
int randomness = [self getRandomNumberBetweenMin:1 andMax:20];
for (int i = 0; i < randomness; i += 1)
{
NSString *npcName = [NSString stringWithFormat:#"anNPC%i", randomness];
NPCclass *NPC = [[NPCclass alloc] initWithName:npcName];
NPC.position = CGPointMake(self.size.width/2, self.size.height/2);
NPC.zPosition = 1.0;
[_worldNode addChild:NPC];
// THIS OBVIOUSLY WORKS. But I can't use this technique outside this method.
[NPC beginMovement];
// THIS IS WHAT I WANT, BUT XCODE DOESN'T ALLOW ME TO WRITE CODE THIS WAY.
[[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
}
}
Is there a way to allow [[_worldNode childNodeWithName:#"anNPC1"] beginMovement]; to work? Or some way similar to this so I wouldn't have to have create an instance variable of NPC (like so: _NPC)?
I'm asking because all of this is happening inside a mini-game scene and NPCclass will be initialized a random number amount of times (with arc4random() method). NPCclass moves on its own using vector (physics in a platformer) movement but I need to initialize its movement method right after creation then I need to periodically access each individually created instance of NPCclass using its name in other methods of the scene. Since I don't know how many NPCclass instances will be created each time the mini-game is played, I CAN'T use IVAR's or something like #property NPCclass *anNPC;
Please help.
Xcode complains about
[[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
because the method -childNodeWithName returns an SKNode object. Instances of the SKNode class do not respond to the selector -beginMovement (or as Xcode puts it, no visible #interface declares the selector -beginMovement). Xcode shows this to you to force you to make sure you wrote what you wanted to write. Since you are sure, you can tell Xcode that the returned object is of the type NPCclass.
(NPCclass *)[_worldNode childNodeWithName:#"anNPC1"]
Now you can expand the statement to call -beginMovement.
[(NPCclass *)[_worldNode childNodeWithName:#"anNPC1"] beginMovement];
Note
There are a few concepts which you might be confusing. NPCclass is a class. +node is a class method of SKNode, which you can call with [NPCclass node];. -beginMovement is an instance method, called with:
NPCclass *npc = [NPCclass node];
[npc beginMovement];
Or:
[(NPCclass *)anyObject beginMovement];
// make sure anyObject responds to this selector though, or you app will crash.
Class methods are prefixed with a +, instance methods with -.
Class methods do not use an instance, just the class name.
As an example consider the
NSString` class method: `+ (id nullable)stringWithContentsOfFile:(NSString * nonnull)path
and a usage:
NSString *fileData = [NSString stringWithContentsOfFile:filePath];

Non-Object Attribute in Core Data, transient properties

Feel lost after reading this section: A Non-Object Attribute
According to the Basic-Approach also contained in above link, I should have 2 attributes in my custom-code when handling "transient properties":
1st attribute, for the actually-wanted (un-supported) custom type => transient attribute
2nd attribute, for shadow-representation (concrete supported) type => persistent attribute
......
My reading was very enjoyable, until reached "A Non-Object Attribute" section, which puzzle me deeply, as quoted below:
...When you implement the entity’s custom class, you typically add an instance variable for the attribute. ...
《 OK, I can follow this...make an iVar is no big deal》
If you use an instance variable to hold an attribute, you must also implement primitive get and set accessors
《 OK, I know how to do primitive-accessor. why need them? because internal-optimized-storage inside MO can be efficiently used, I guess.》
#interface MyManagedObject : NSManagedObject
{
 NSRect myBounds; // I assume this suppose to be the **transient attribute**
}
#property (nonatomic, assign) NSRect bounds; // I assume this is the **persistent attribute**
#property (nonatomic, assign) NSRect primitiveBounds; // because complier forces me to implement below primitive-accessors ?
#end
- (NSRect)primitiveBounds
{
return myBounds; // accessing iVAR storage for **transient attribute**? I hope so
}
- (void)setPrimitiveBounds:(NSRect)aRect
myBounds = aRect; // accessing iVAR storage for **transient attribute**? I hope so
}
From here down below, I have... too many ???????????? unsolved
- (NSRect)bounds
{
[self willAccessValueForKey:#"bounds"]; //KVO notice of access **persistent attribute**, I guess
NSRect aRect = bounds; //will this invoke primitive-Getter ???
[self didAccessValueForKey:#"bounds"];
if (aRect.size.width == 0) //bounds has not yet been unarchived, Apple explained
 {
NSString *boundsAsString = [self boundsAsString]; // unarchiving pseudo method, I guess
if (boundsAsString != nil) //if that value is not nil, transform it into the appropriate type and cache it...Apple explained.
{
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
}
}
return bounds;
}
I put my final question list here:
1, do I STILL need to have 2 attributes to handle NON-Object-Attribute, transient attribute and persistent attribute?
2, how can iVar "myBounds" be represented/connected with "#property bounds"? Is this "#property bounds" the modeled-property in a MOM?
3, what is the purpose of implementation of primitive-accessor here? for enforcing me write KVO (will...did...) methods pair? for transferring values (in and out) between iVar "myBounds"and "#property bounds"?
4, in this line of code
bounds = NSRectFromString(boundsAsString); //will this invoke primitive-Setter???
is primitive-Setter called OR public/standard-Setter gets called? Why?
In iOS, there are the very convenient NSStringFromCGRect and CGRectFromNSString functions. Why not just use those and store a string?
Your questions:
Yes, you need the 2 attributes, as explained in the documentation.
Yes, this is based on the managed object model. The primitiveX name for x is generated / interpreted automatically.
You need the primitive accessor methods here to make it KVC - which is not the case with primitives.

OCMock stub isSelected property

I am trying to stub the selected property on UIButton. The getter is defined as:
#property (nonatomic, getter=isSelected) BOOL selected;
My stub looks like this:
[[[button stub] andReturnValue:OCMOCK_VALUE(TRUE)] isSelected];
I receive the following error when I run the test:
Return value does not match method signature; signature declares 'c' but value is 'i'.
I think this is something to do with the getter=isSelected part but not sure what's going on
Is it possible to stub this type of getter?
This is annoying. The problem is that passing TRUE to OCMOCK_VALUE results in the creation of a value of type integer. The message you get is OCMock's way of saying that the method/property you want to stub is a boolean but you provided an integer.
You can force the creation of a an actual boolean value with either of the following:
[[[button stub] andReturnValue:OCMOCK_VALUE((BOOL){TRUE})] isSelected];
[[[button stub] andReturnValue:#YES] isSelected];
By the way, a similar problem occurs with different number types but unfortunately fixing this in OCMock isn't trivial. See here for example: https://github.com/erikdoe/ocmock/pull/58.
This doesn't answer my question but incase anyone else stumbles across this problem a workaround is to do a partial mock of an actual instance of UIButton.
UIButton *button = [[UIButton alloc] init];
button.selected = TRUE;
id mockButton = [OCMockObject partialMockForObject:button];

Resources