Objective-C's UIView and animationIDs - iphone

What are all the valid animationID strings for the beginAnimations:: class method on UIView?

The animationID is any string you decide to use. It's for identifying the animation in the animations's willStart and didStop delegate methods.
From the documentation:
animationID Application-supplied
identifier for the animations within a
block that is passed to the animation
delegate messages—the selectors set
using the
setAnimationWillStartSelector: and
setAnimationDidStopSelector: methods.

Related

Swift Delegate (contactDelegate) explanation

I've research a lot on what delegate means but somehow my understand doesn't fit with this explanation. This is from a tutorial:
physicsWorld.contactDelegate = self
and this is his explanation: "This sets up the physics world to have no gravity, and sets the scene as the delegate to be notified when two physics bodies collide."
I thought delegate is just a class that uses a protocol. Does anyone have a different explanation for this? what exactly is that line do? Thanks.
(ignore the gravity part, there's another line of code)
According to the Swift documentation for the PhysicsWorld class:
contactDelegate Property
A delegate that is called when two physics bodies come in contact with
each other.
Therefore when we see the line:
physicsWorld.contactDelegate = self
We are setting the contactDelegate property on the physicsWorld instance so that when two physics objects collide some method will be called i.e. the delegate.
By assigning self we are delegating the responsibility of responding to physics object collisions to the class (GameScene).
override func didMoveToView(view: SKView)
Because we are overriding this method in the GameScene class which inherits from the SKScene class we are essentially saying:
"Don't use SKScene didMoveToView() use this GameScene version of didMoveToView()" (because self refers to the GameScene class).
So the function "didBeginContact()" is a delegate? and contactDelegate always has to be the class that's going to implement the "didBeginContact()" function (or any other function from the delegate protocol)?
I assume that the didBeginContact() method calls the method that is set to the contactDelegate property.
Java does not have delegates. A delegate is like a variable except it stores a method. You can use the variable to then call the method. (If you know some C it is a pointer to a function)
This helped me understand delegates when I first came across them.
A delegate is a protocol, also sometimes called a "delegate protocol". The class containing the quoted line of code becomes the delegate and gets the messages of the protocol in question. The physicsWorld will contact its delegate to inform it of a collision. If the delegate methods are implemented (i.e. the controller conforms to the delegate protocol), it can take appropriate action when it is notified.
Clear?

Anybody successful with NSProxy of UIView (e.g., UILabel?)

I am experimenting in adding functionality to my UIViews (configuring CALayers according to state) by setting up a NSProxy subclass to stand in for any UIView I choose. Here's what I've tried:
In my NSProxy subclass, I have the following code:
#pragma mark Initialization / Dealloc
- (id)initWithView:(UIView *)view
{
delegate = view;
[delegate retain];
return self;
}
- (void)dealloc
{
[delegate release];
[super dealloc];
}
#pragma mark Proxy Methods
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
[anInvocation setTarget:delegate];
[anInvocation invoke];
return;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
return [delegate methodSignatureForSelector:aSelector];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
BOOL rv = NO;
if ([delegate respondsToSelector:aSelector]) { rv = YES; }
return rv;
}
And, using my NSProxy subclass this way:
UILabel *label = [[HFMultiStateProxy alloc] initWithView:[[[UILabel alloc] initWithFrame:cellFrame] autorelease]];
label.text = text;
label.font = font;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
label.opaque = NO;
[self addSubview:label];
Seems to work until I hit the addSubview: line.
Turning message tracing on ( instrumentObjcMessageSends(YES); ) shows the forwarding for each of the previous messages working until deep inside of the addSubview:, where this series of method calls show up in the log (the first message shown here was invoked via the proxy):
- UILabel UIView _makeSubtreePerformSelector:withObject:
- UILabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- NSMethodSignature NSMethodSignature methodReturnType
- NSMethodSignature NSMethodSignature _argInfo:
- NSMethodSignature NSMethodSignature _frameDescriptor
+ UILabel NSObject resolveInstanceMethod:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject forwardingTargetForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject methodSignatureForSelector:
- UILabel NSObject class
- UILabel NSObject doesNotRecognizeSelector:
And I get the following error:
2011-02-20 16:38:52.048 FlashClass_dbg[22035:207] -[UILabel superlayer]: unrecognized selector sent to instance 0x757d470
if I do not use an NSProxy subclass and instead use a UILabel subclass (HFMultiStateLabel), it works fine. Here is the message trace that occurs once addSubview: is called (HFNoteNameControl is the superview of the label):
- HFNoteNameControl UIView addSubview:
- HFNoteNameControl UIView _addSubview:positioned:relativeTo:
- HFMultiStateLabel UIView superview
- HFMultiStateLabel UIView window
- HFNoteNameControl NSObject isKindOfClass:
- HFNoteNameControl NSObject class
- HFNoteNameControl UIView window
- UIWindow NSObject isKindOfClass:
- UIWindow NSObject class
- HFNoteNameControl UIView _shouldTryPromoteDescendantToFirstResponder
- HFMultiStateLabel UIView _isAncestorOfFirstResponder
- HFMultiStateLabel UIView _willMoveToWindow:withAncestorView:
- HFMultiStateLabel UIView _willMoveToWindow:
- HFMultiStateLabel UIView willMoveToWindow:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- HFMultiStateLabel UIView willMoveToSuperview:
- HFMultiStateLabel UIView _unsubscribeToScrollNotificationsIfNecessary:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:
- HFMultiStateLabel UIView _makeSubtreePerformSelector:withObject:withObject:copySublayers:
- CALayer CALayer sublayers
- CALayer CALayer superlayer
I can verify that each of the methods up until -superlayer are called successfully when using NSProxy. For some reason, with the NSProxy, superlayer on UILabel is being called instead of CALayer. Perhaps somewhere something gets confused and UILabel is inserted into the sublayers instead of its CALayer?
Does the UIKit do some sort of optimizations that bypass the normal mechanism that NSProxy hooks into?
PS I have only tried this in the Simulator, not the device. Would that behavior be any different?
I was trying to solve the same issue - use NSProxy with UIView (in my case UITableViewCell) when I encountered this problem. I logged all calls to the console:
...
App[2857:c07] MyHeaderCell: --- method signature for: _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- _unsubscribeToScrollNotificationsIfNecessary:
App[2857:c07] MyHeaderCell: --- method signature for: _makeSubtreePerformSelector:withObject:
App[2857:c07] MyHeaderCell: --- _makeSubtreePerformSelector:withObject:
App[2857:c07] +[MyHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] CRASH: +[SMSHeaderCell superlayer]: unrecognized selector sent to class 0x1331f8c
App[2857:c07] Stack Trace:...
It crashes on the unrecognized selector exception.
Normally, the object is asked the - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel method first and when that is returned, it invokes the - (void) forwardInvocation:(NSInvocation *)invocation in the proxy. This way we can redirect the messages. If there is not NSMethodSignature returned, the doesNotRecognizeSelector: method is called on the object. So we get even unrecognized selector calls.
This works for instance methods, but this crash is caused by a class method, which we have no power over - the object itself is not called (the class is). I wanted to force the runtime to call my proxy class even for class methods by overriding the getter of my NSProxy subclass
- (Class) class
{
return _myRealClass;
}
Which did not work. So NSProxy is not enough to do this. Right now I'm trying to use the NSObject instead of NSProxy to achieve all the desired behavior and since NSObject has the + (BOOL)resolveClassMethod:(SEL)sel method which might be useful. I will edit this post once I found out if NSObject is better suited for this.
//Edit
It seems that the problem is that with NSProxy, superlayer is being called on UIView instead of CALayer.
So it really seems like a UIKit shortcut problem - they're not sending a regular message call (speed optimization I would guess).
Anyways, this I am searching for a way to get around this now.
I gave up trying. I've come to the conclusion that NSProxy is such an underused object that it's potential for uses beyond Apple examples has not been fully explored nor debugged. In short, I believe that NSProxy is not ready to be used as a generic way to extend an object's functionality without subclassing or adding a category.
In the old days, I would have used a poseAsClass call to implement my desired functionality.
My solution ended up something like this:
I added a category to UIView that added additional properties. These property implementations forwarded their set & get messages to a "addOn" property of the UIView that I also put into the category. The default value of this "addOn" property in the UIView's category implementation is, of course, nil. (I could have implemented a static hash table to enable associating an AddOn instance for any UIView, but it struck me as a risky ploy to manage with the retain counts properly.)
The "AddOn" class had extra code in it to directly manipulate the UIView, and it added extra drawing code in it.
For each type of UIView that I wanted to add this added functionality, I had to subclass it with code that:
a) Created an instance method and corresponding property code for the "AddOn" class
b) Subclassed any functions I covered to give the "AddOn" code a chance to add its functionality.
Each of these subclasses has essentially the same code in it to forward the desired functionality to the AddOn instance.
SO, I ended up minimizing code duplication as much as I could, but each of the UIView's descendant subclasses that enable use of the the "AddOn" functionality ends up duplicating code.
It appears that I could have further minimized code duplication by using class method manipulation functions, but that learning curve and further obfuscation of the code deterred me from following that path.
I have never tried using NSProxy with views, but I have done something similar by using a custom view class to display another view. Maybe the system requires an actual view and not a proxy object. There are two ways you could use a "proxy" view:
Make the proxied view a subview of the proxy view. The proxy would take the frame, autoresizing mask, etc. from the proxied view, then add the proxied view as its subview and set its frame to be the proxy view's bounds, and its autoresizing mask so that it always fills the proxy view. When the proxied view is removed, any settings are copied back into it from the proxy view. Any properties not copied into the proxy are passed to the proxied view using forwarding.
The proxy view passes almost every message to the proxied view. The proxy view does not override the lock/unlockFocus, display, etc. methods. It overrides drawRect: to call drawRect: on the proxied view.
After trying the same thing, and searched for the error (which got me here), I tried to circumvent the problems... It wasn't pretty.
Identifying the root problem was easy. Somewhere in the framework, Apple is using direct pointer access to the variables in UIView subclasses. If you check the headers, the variables are declared with #package access identifier.
What I basically tried was:
Create a proxy class at runtime with ivars copied from the UIView class definition, and then set the values of these pointers to the objects in the UIView. Couldn't get far there.
Declare just the CALayer * in the proxy subclass, and only copy that pointer from the protected UIView instance. Worked, but I think it was buggy? It didn't work with auto layout at all, though, so I decided to move away from that solution.
The code I tried can be found in the RTLSegmentedControl repo under the proxy-pattern branch
I also wrote a blog post about the details.

What are the two options that come with the core animations beginAnnimations method?

I was wondering what they mean by:
[CustomView beginAnimations:#"whatIsThis" context:whatIsThis];
I am just wondering what those who things do and how I would use them?
Please help!
They are basically application-specific helper objects. animationID is used to distinguish between the different animations (that could be happening around the same time). context is additional object that gets passed to the delegates and listeners of the animation status. Both are optional, and passing nil should work.
From documentation:
animationID
Application-supplied identifier for the animations within a block that is passed to the animation delegate messages—the selectors set using the setAnimationWillStartSelector: and setAnimationDidStopSelector: methods.
context
Additional application-supplied information that is passed to the animation delegate messages—the selectors set using the setAnimationWillStartSelector: and setAnimationDidStopSelector: methods.

Creating an object to act as a delegate - Objective C

My question is simple actually, how do I create an object to act as a delegate, instead of including the delegate methods in my view?
For example, I have x functionality that requires delegate methods, and they're currently setup to use self as the delegate. I'd like to put those methods in their own object so that the delegate methods can be called and do stuff if the view has ended.
What's the best way?
for example, NSXMLParser delegate methods - they exist, the delegate is defined, but I dont want to call them as self in my view controller... what other option do I have?
You can specify another custom class to handle the delegate methods, if you wish. Simply create a class, call it MyXMLParserDelegate or something similar. Then, all you have to do is tell your NSXMLParser object that it should use an instance of your class as its delegate.
If you are using Interface Builder, add a new object to the XIB file, set its class to MyXMLParserDelegate, and then drag a connection from your NSXMLParser object's delegate selector to the new object.
If you are doing it programmatically, the basic operation looks like this:
MyXMLParserDelegate * myDelegate = [[MyXMLParserDelegate alloc] init];
[someXMLParser setDelegate:myDelegate];
Keep in mind, however, that delegates are not retained, so in order to do this without leaking memory, you should add an ivar of type MyXMLParserDelegate to your viewController class, and then do the following:
// in your #interface block:
{
...
MyXMLParserDelegate * myDelegate;
}
// in your init method:
myDelegate = [[MyXMLParserDelegate alloc] init];
// in your awakeFromNib method (or anywhere else it seems appropriate):
[someXMLParser setDelegate:myDelegate];
// in your dealloc method:
[myDelegate release];
Check out this answer, I think it covers what you need: How to use custom delegates in Objective-C

I need help with Animation Callbacks (iPhone)

I am creating an application in iPhone and I have several UIViews and layers in it. I am doing some animations using CAKeyframeAnimation class and since the animations have to be chained, I have overridden the animationDidStop method in UIView.
I am getting the callbacks properly, however I just couldn't figure out how I can find which animation was ended so that I can start the next one. Only parameters to the callback function is a CAAnimation object and a boolean.
I can workaround this problem by setting a property in the class and using an enum for the various animations I use. However I just wanted to know if there is any built in attributes in the callbacks which I can set in the CAKeyframeAnimation object and then refer the same in the callback.
Any help would be greatly appreciated!
You can specify a name for an animation and read it in your delegate method.
[animation setValue:"firstAnimation" forKey:#"name"];
...
- (void)animationDidStop:(CAAnimation*)animation finished:(BOOL)finished {
if([[animation valueForKey:#"name"] isEqual:#"firstAnimation"] && finished) {
...
}
}
I know that you said that you're using CAKeyframeAnimations, but if you want simple animation of UIView properties (origin, bounds, alpha, etc.), you can wrap the change of the property or properties in a begin / commit block and specify a delegate method that is called upon completion of the animation. As long as the delegate method takes three arguments, you can call it whatever you want. For example:
[UIView beginAnimations:nil context:NULL];
[UIView setAnimationDuration:ANIMATIONDURATIONINSECONDS];
[UIView setAnimationDelegate:self];
[UIView setAnimationDidStopSelector:#selector(yourAnimationHasFinished:finished:context:)];
// Change property or properties here
[UIView commitAnimations];
will cause the method
- (void)yourAnimationHasFinished:(NSString *)animationID finished:(BOOL)finished context:(void *)context;
to be called. The arbitrary naming this allows would provide you with a means of separating handling for the completion of different animations. I prefer this for simple animations.
For dealing with more complex animations that interact directly with CALayers, the animationDidStop:finished: delegate method does return the animation object that has finished. If you are making one instance that is the delegate for multiple animations, you could create an NSMutableDictionary of animations and NSNumbers for use in a switch statement within the animationDidStop:finished: method. As you create the CAKeyframeAnimation, use setObject:forKey: to assign it to its matching number, then use objectForKey: to find the number that corresponds to that animation in the completion method and feed that into a switch statement.