Where do FirstResponder methods come from? - objective-c

I'm looking at IKImageDemo supplied by Apple, the rotate round-slider is linked to a setRotation: method in the FirstResponder. However, none of the objects in the project seem to HAVE such a method, and yet the code works.
I'm trying copy this into my own project, and MY FirstResponder doesn't have a setRotation: method, so I'm not sure where it lives. Google has been unhelpful...
thanks.

Well, the first responder in the app happens to be an instance of IKImageView. IKImageView responds to the setRotation: selector (which can be seen by passing respondsToSelector:#selector(setRotation:) to any instance of IKImageView), although I cannot find where in documentation it mentions the setRotation: method

First Responder methods aren't magic. What happens when a message is sent to the first responder is that the app's current first responder (this is usually the focused view/control) is asked whether or not it implements the method. If it does, the method is called. If it doesn't, the next responder up the chain is asked, and so on until the top level (the NSApplication instance) is reached. The object must actually implement the method for it to be called, it can't just declare it.
In this case IKImageView implements -setRotation: as a private method. This means that the method is present (which is why the IKImageView accepts the message sent to the First Responder) but its use is not documented or supported. It seems odd that Apple would ship an example using a private method but there you go. It's definitely the case that sometimes methods are accidentally left out of the public headers when their use is supported, however it's generally wise to avoid private methods unless someone from Apple has specifically told you it's OK to use one.
You can generate headers for all methods of an Objective-C object, including private methods, from the binary using class-dump.
IKImageView has a public method -setRotationAngle: which is probably the way to go if you want to change the rotation.

I've found a way of resolving this annoyance. Even in the original Apple example, once you remove the binding for setRotation in the First Responder, you cannot put it back, unless doing this trick: simply use the Attributes Inspector for the First Responder and add a User Defined action "setRotation:" with type "id". Now even the yellow triangle in the First Responder binding for setRotation: in the Apple example disappears, and it shows up also in my own IKImageView instance.

Related

How do I explicitly send key-value observing notifications from an NSResponder?

I'm trying to make a NSTouchBar in an SDL application and I need to attach a responder to the NSWindow object (that's the only access SDL gives into the Cocoa windowing system).
https://developer.apple.com/reference/appkit/nstouchbar
If you explicitly adopt the NSTouchBarProvider protocol in an object,
you must also explicitly send the associated key-value observing
notifications within NSTouchBar methods; this lets the system respond
appropriately to changes in the bar.
What does that mean and how do I do it? I see lots of documentation about how to subscribe to the notifications, but not how to send them?
Right now I have:
#interface MyTouchBarResponder : NSResponder <NSTouchBarDelegate>
- (id)init;
- (NSTouchBar *)makeTouchBar;
- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier;
#property(strong, readonly) NSTouchBar *touchBar;
#end
and I'm attaching it to the window with the code from a previous question I asked here: How to create an NSTouchBar from an NSWindow object?
touchBarResponder.nextResponder = window.nextResponder;
window.nextResponder = touchBarResponder;
but my callbacks aren't ever being called (I put exit(0) in them to make it very obvious). When I hack the code directly into the SDL library, things work as expected, but that's not a viable permanent solution.
Thank you.
First, your custom responder should conform to NSTouchBarProvider (in the above, you declare the touchBar property, but not the explicit conformance)
Second, you want to make sure that your custom responder is in the responder chain of the window (whether the first responder or just later in the chain). After adjusting the responder chain with your above code, you want to call -makeFirstResponder: and pass in some view in the window (if you need that view to be first responder) or with the custom responder object. You should then verify that the window's firstResponder is that object.
With these in place, you should get at least one call to touchBar after the window is shown and made key.
To answer the question on key-value observing notifications, that is needed for when you want to change the actual NSTouchBar object being returned from touchBar. In the general case this isn't necessary, since it's unnecessary in the static touch bar case, and even in the dynamic case, you can rely on just setting the defaultItemIdentifiers on the previously created touch bar and it will update. However, should you need to change the touch bar object, you need to ensure that -willChangeValueForKey: and -didChangeValueForKey: are sent for touchBar when you change the return value. This developer documentation on KVO goes into much more detail.

How does forwardingTargetForSelector: work?

I have a UIBarButtonItem. When it receives a message it cannot handle, I want it to forward that message to a particular view controller.
I thought I might be able to accomplish this using the bar button item's forwardingTargetForSelector: method, but apparently no such property is found on objects of type UIBarButtonItem. (Point of terminology: Does that mean forwardingTargetForSelector: is a private property? edit: Wait, I think I'm confused... methods with a colon at the end aren't properties... so can you ever make public a method (like a getter/setter) to which parameters are passed?)
And does that mean that in order to set the value of forwardingTargetForSelector: I must do it from within the .m file of the object for which I want to set it? Which would mean that I would have to subclass my UIBarButtonItem?
And if so, why is this not a public property of NSObjects?
And moreover, what's the best way to achieve my forwarding goal, preferably avoiding subclassing?
additional information:
It all stems from my inclination to reuse a single action in response to various instances of an identical button being pressed. The action is currently contained in my delegate (see How should I implement [almost] identical actions used throughout various VCs? (Answer: use a category)) and varies only in that it should send a presentViewController message to the view controller that instantiated the button that sent the action. Thus, in the action, I can send a presentViewController message to sender, which is an instance of the button, and I want to be able to forward that message to the view controller that created that instance of the button, which I can do if I set each button's forwarding property immediately after it is instantiated in its respective view controller.
I hoped to avoid the "why" just to make the question shorter, but there ya go.
forwardingTargetForSelector: is not really a property; it's more like a question the runtime asks an instance when the instance doesn't respond to a message.
It can't be a property in the #property/declared-property sense, because each selector could have a different target; there would need to be a mapping behind it. That's just not how declared properties work.
UIBarButtonItem descends from NSObject, and it inherits this method along with all the others, but you can't "set" the forwarding target for a selector from outside an instance (without creating some other machinery to allow you to do so, anyways -- possible, but not available by default).
In order to utilize this method, yes, you have to implement it in the class that is doing the forwarding. This does indeed mean subclassing. It also means that the forwarding instance needs to have a reference to the object to which it is forwarding; this requires careful design.
forwardingTargetForSelector: is all but certainly not the correct way to achieve whatever your goal is. In general, in fact, it's a bit esoteric.
I'm not sure exactly what problem you're trying to solve ("making a button forward messages it doesn't respond to" is still rather general -- in particular, why is that necessary?), so it's hard to be more precise.

How do we implement selectionRectsForRange: from UITextInput Protocol?

How do we implement selectionRectsForRange: from UITextInput Protocol ?
Has anybody figured out this one?
Is it just very dependent upon specific use-case needs? Or is there something in the frameworks that will call this method?
To silence the compiler it is of course appropriate to stub out the method, but will returning nil or an empty NSArray cause any harm?
According to session 220 at WWDC12 this method was added to support subclassing of UITextView where the implementation renders its own text. Sadly their sample code from that session isn't available, would love to peek at it to see if I've missed anything in my implementation.
It's fairly similar to how you'd implement -firstRectForRange: except you'd return all rects which covers the current selection.
Furthermore you'd have to subclass UITextSelectionRect (it's an abstract class like UITextPosition/UITextRange) which you'd return an array of from this method. Make sure to calculate the containsStart and containsEnd properties correctly and only return YES for one of each once across all the selection rects you return. These properties are used by UITextView to decide on where to place the selection resize "paddles".
Returning an empty array (or nil I suppose) would indicate that UITextView shouldn't draw any selection rects for the current selection.

What's the perferred event to handle the end of user interaction with a UIControl?

I have a view with multiple dynamically created UITextfields and UISegmented controls on it (but for purposes of this question, there could also be UIButtons, UISwitches, UISliders, or anything else that inherits from UIControl). I want to preform an action whenever the user finished interacting with any of the controls, regardless of what subclass of control it belongs to. From looking at other questions, I think I want to use addTarget:action:forControlEvents: to add observers to each of my controls after they are created, but I don't know which event I'm looking for. I've tried all the ones that are listed in the Apple Docs here that seemed relevant but none of them seem to be triggered everytime. I'm looking for something like .LostFocus in VBA, but I can't seem to find out what that is - I know there is a becomeFirstResponder method to make a control active, but I can't find anything like a "lostFirstResponder" event.
I suppose I could use isKindOfClass to tell what kind of control it is, and set up my event accordingly, but that seems a little sloppy and I feel like there should be a more direct way to do it. I could also probably set up a UITapGestureRecognizer and build up something that way, but that still feels like a workaround and not really the way it's supposed to be done.
If you're willing to subclass, you can override -resignFirstResponder to detect lost "focus", and act accordingly. This is probably only useful for things like textfields which can hold first responder status, and would not work for UISwitch for instance.
Since all UIControl objects are just UIViews, you can also override touchesEnded to detect the end of interaction with these elements.. although the more accepted way is to add your dismissal handler method as an action for all the UIControlEvents that indicate end of interaction, or just UIControlEventValueChanged.
More info on UIResponder here from Apple's Documentation:
https://developer.apple.com/library/ios/documentation/uikit/reference/UIResponder_Class/Reference/Reference.html#//apple_ref/occ/instm/UIResponder/resignFirstResponder
Many UIKit classes have delegate methods that indicate when interactions have ended, for instance UITextField has a textFieldDidEndEditing method. UITextView has similar methods.

multiple delegates in objective-c

i have been working on moving one of my apps away from the "shared appdelegate" process which seems to be frowned up, despite its over whelming use. i have been attempting to setup protocol methods for what i want to do but am having zero luck. my question is, can you even have lets say a single viewcontroller send delegate requests to multiple classes? from what im finding out it doesn't seem like you can. which doesn't make sense because i thought that was the whole point of delegates and protocols with mvc. now just to clarify, i know you can have a single viewcontroller act as the delegate for multiple other viewcontrollers. but that's not what i am asking. for a simple example, lets say you have apples flip-utility template. the "done" button just calls a delegate method to the mainvc to dismiss it. now lets say we added a new class called...
#interface NewClass : NSObject <TheOtherDelegate>
and it had a delegate method...
- (void)doSomething
{
NSLog(#"The Delegate did something...");
}
can we have a button on the flipsideviewcontroller, that we wanted to call that delegate method, but still keep the "done" button call to the delegate method on the mainviewcontroller that dismisses it?
that being said, i put together a quicky project just to see if it would work and it doesn't. i came across an "answer" that says you have to instantiate the class first you want to be the delegate...
NewClass *myDelegate = [NewClass alloc] init]
[fillInMethodHere setDelegate:myDelegate];
not sure why it got a correct answer check, because needless to say it doesn't work. is there something i am missing? i scoured ib to see if there is some "delegate" connection somewhere but i couldn't find anything.
on a side note, as i was working in my working project, i read a suggestion about removing the #import and adding #class. again, that broke all kinds of things. the strange thing is before doing that, what i had so far was working and building fine. when i removed the new #class and un-commented the #import. xcode all of a sudden gave me an error "cannot find protocol deceleration for..." but yet, it worked seconds earlier. i would up having to remove the protocol code and re-add it for it to work again. very starge.
any help would be appreciated. everything iv read in docs, google, stack, etc that say something should work, don't in an actual project.
A "delegate" isn't some fancy object. It's simply a synthesized property of type id called delegate. If you wanted to, you could have an arbitrary number of properties that all conformed to the same protocol. Then when you wanted to issue a callback, you would just address all of them:
[self.mydelegateA doSomething];
[self.mydelegateB doSomething];
etc.
You could also have an NSMutableArray property that you could add objects to, and then use [self.myMutableArrayOfDelegates makeObjectsPerformSelector:#selector(doSomething)].
Finally, there's always the route of NSNotificationCenter (not to be confused with push notifications) is a class that provides an inter-object messaging system. Many objects can register for a message that any other object can send.
Please see the Apple's documentation for more information. Click Here.
Regardless of the fact that this is OS X documentation, it's still quite good at explaining things visually: click here.
Here's an example of simply changing the name of the delegate property: click here
And here's an example of adding another protocol and a second delegate: click here
Finally, here's an example that builds on the previous two and has a third delegate that also conforms to the same protocol: click here