I am trying to develop an Cocoa Touch Framework or a dynamic Library.
This Framework should intercept the touch, swipe, pan gestures on views. For starting let us assume that I want to intercept touch event on every control (UIButton, UIView etc. ) and it should be captured in the Framework where I would use that data for further processing.
Could anyone direct me in correct direction about how to target this?
If you want to replace methods in UI classes you can replace methods in base classes (UIView or UIViewController). If you do not want to call super inside a code of your classes. You should swizzle methods inside any of your subclass.
To achieve this, I see two solutions:
1. First, create custom subclass for UIView, UIControl, UIViewController. These subclasses should have swizzled methods and don't call super.
#interface CustomViewBase
#end
#implementation CustomViewBase
+ (void)initialize
{
[SwizleLib swizleMethodsInClass:[self class]];
}
- (BOOL)touchBegin<.....>
{
return YES;
}
#end
And you will create subclasses of CustomViewBase and call super implementation for any methods. (Don't forget that you should create subclasses for UIView, UIControl, UIViewController, UIButton, UITableViewCell).
2.If you do not want to change a base class (you want to use UIView as subclass for your UI classes), you should swizzle methods in your subclasses (since, you do not want to call super).
Related
So I have an NSObject that accepts a UIView in the init function, and assigns it as a property. The functionality of this behavior class depends on how long the UIView is pressed, using touchesBegan/touchesEnded.
I want to encapsulate this touchesBegan/touchesEnded logic within the NSObject class, in an effort to make this behavior class more modular. Currently, the touchesBegan/touchesEnded functionality is in the UIViewController class, and passed to the behavior.
Is it possible to override and/or "listen" for touchesBegan/Ended in the NSObject class?
For custom touch handling logic you should subclass UIGestureRecognizer. You can then add it to any view.
Check out the documentation for gesture recognizers and the guide.
Subclass of UIView
I have a subclass MyView of UIView.
This subclass has a #property UIView * realView.
What I want to do
Whenever a message is sent to MyView, I want to "forward it" to self.realView, excepted for few messages.
For instance, in the implementation of MyView, I would have this override:
- (void)setBackgroundColor:(UIColor *)color
{
[self.realView setBackgroundColor:color] ;
}
Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?
Exceptions
For some methods, I want to have an explicit control. For instance:
- (void)setFrame:(CGRect)frame
{
/* do stuff */
[super setFrame:frame] ;
}
Instead of overriding explicitly all the methods, can I do it automatically, at the runtime?
You implement the -forwardInvocation: method to send any unrecognized messages to the other object. -forwardInvocation is called whenever an object doesn't implement the selector that's passed to it as a sort of second chance to handle a message. You can override it to send the message to another object (which is pretty much what NSProxy does), log the messages, etc.
As #cobbal points out below, -forwardInvocation will help you with methods not implemented in your superview, but it won't handle methods that are implemented int the superview because your MyView inherits implementations of those. For example, if you want to use a UIView as a proxy for a UIButton, all the methods specific to UIButton can be handled by -forwardInvocation:, but those defined by UIView cannot. In order to get a behavior other than the inherited method, you will of course need to override. In some situations you can get around that by deriving MyView from NSObject or UIResponder instead of from UIView, thus avoiding the inherited UIView implementations, but if MyView needs to be a real view you're stuck with overriding each method.
If you think about it, it's hard to imagine how your goal could be met without explicitly overriding each inherited method. You say that you only want to forward most messages, but how can the poor runtime tell which ones you do want to forward and which ones you don't? All it can do is look for a method for the given selector and call it if it finds one, or take some action (like calling -forwardInvocation:) if it doesn't.
Update: #robmayoff points out -forwardingTargetForSelector:, which didn't occur to me but is probably a better solution in your case. It still doesn't handle the situation where you need to redirect methods that you inherit from a superclass, though.
It's entirely possible.
First you need WZProtocolIntercepter. Then use the intercepter as the normal UIView:
WZProtocolInterceptor* fakeView = [[WZProtocolInterceptor alloc]
initWithInterceptedProtocol:#protocol(TheMethodsForTheMiddleManToHandle)];
fakeView.receiver = self.realView;
fakeView.middleMan = self;
[someViewController.view addSubview:fakeView];
Put the methods you want to control in TheMethodsForTheMiddleManToHandle:
#protocol TheMethodsForTheMiddleManToHandle
- (void)setFrame:(CGRect)frame;
#end
This question already has answers here:
Is it possible to make the -init method private in Objective-C?
(9 answers)
how to block a superclass method to be called to a subclass
(5 answers)
Closed 9 years ago.
Suppose you have a UIView subclass. You define an init method "myInitWithFrame: ... andWhatNot:...". You know you won't be using the init method inherited from UIView ever and your custom init method does some vital custom initialising so that you want to force client classes to never use the inherited initWithFrame method.
Is it possible to hide the standard initWithFrame method that was inherited from UIView?
Actually, you can get compile-time warnings about calling a method on a subclass. Use the __attribute((deprecated)) attribute. If you want people to use -initWithPizza: instead of -initWithFrame:, do this:
#interface MyView : UIView
- (id)initWithPizza:(MyPizza *)pizza;
#end
#interface MyView (Deprecations)
- (id)initWithFrame:(CGRect)frame __attribute((deprecated("Use initWithPizza: instead")));
#end
Putting the -initWithFrame: declaration in a separate category is necessary to avoid Xcode complaining that you declared the method in the header but didn't implement it. Since you're just inheriting it from the superclass, that's fine; you don't have to implement it at all. But if you want to implement it to throw an exception, or call through to -initWithPizza: with a default argument, that's fine.
Of course, this won't stop UIKit from calling -initWithFrame: if it was already going to do so. But if you can guarantee that won't happen, then you're fine.
Actually, you CAN restrict with a subclass. You can override whichever methods you want to block in your subclass's .h file. You can make initWithFrame unavailable by placing the following in your .h file.
- (id) initWithFrame:(CGRect) frame __attribute__((unavailable("message")));
This will make the initWithFrame: method unavailable to anyone using your subclass.
To keep other code form calling this method, you can further restrict by putting this in your .m file.
- (id) initWithFrame:(CGRect) frame
{
return nil;
}
No. You can't prevent the users of your subclass from calling the methods of a superclass. You could override them and throw an exception inside, but that would just produce a broken subclass.
Remember that inheritance works as an "is a" extension, that is, instances of your subclasses should behave normally in any context that doesn't know about this particular subclass but knows about its superclass. It's only in places that have explicit knowledge about your subclass that you can benefit from adding extra initialization and other methods.
For example, UIKit has no knowledge of your subclass. So if you want to make your UIView subclass available from a NIB, you need to use the initialization methods that will be called by the NIB loading system, namely initWithCoder:. You can simply call your own initialization methods inside initWithCoder:. But if there are any additional parameters you would like to pass to the init method, you'll have to provide a way to configure them after initialization.
I'm working on an accessibility project for an iOS application. Because accessibility does not act quite as advertised, I have to override accessibilityFrame, accessibilityActivationPoint and pointInside:withEvent in a subclass in order to expand the region recognized by VoiceOver (for both drawing and touch recognition) beyond the "natural" bounds of the control view. So, in order to change the VoiceOver bounds of a UIButton I have to subclass that class and then add these three methods. In order to do this for a UILabel I have to add another subclass with the code, and so on.
I can refactor the code in these methods to a central location, but I was wondering if this can be done more elegantly with inheritance. I'd like to put this code into a subclass of UIView (maybe called UIViewAccessible) and then create a subclass of UIButton called UIButtonAccessible which inherits from UIButton which would in turn inherit from UIViewAccessible instead of UIView. Is this possible, or can something like this be done with a category?
Edit: According to the docs, you can't really achieve this with a category:
If the name of a method declared in a category is the same as a method in the original class, or a method in another category on the same class (or even a superclass), the behavior is undefined as to which method implementation is used at runtime.
Is there some other way to do this?
To answer your question, no, it can't, since your UIViewAccessible is a second degree sibling to UIButton in the inheritance chain (both inherit from UIView at some point). But I guess you already knew that. As for a solution, you could wrap around your UIView accessible classes a decorator and use protocols for strong typing. That way you'll keep the code in one place. I've described this technique here in more detail (although for a different purpose, it's the same situation).
For the views that would support accessibility you'll have to do this:
#property (nonatomic, strong) UIView<MyAccesibilityProtocol>* view;
//self.view can come from the nib or previously created in code
self.view = [[AccesibilityDecorator alloc] initWithDecoratedObject:self.view];
//you can then use self.view like any other UIView,
//and because it also implements an
//accessibility protocol, you can use the methods
//implemented in the wrapper as well.
//more than that, you can control which methods to override
//in the AccesibilityDecorator class
[self.view addSubview:otherView];//could be overridden or not
[self.view myAccesibilityMethod];//custom method declared in the protocol
In my application I want to associate NSMutableDictionary to all UIElements (UIButton, UILable, etc), I can easily achieve this by subclassing each element, but I just want to know if I can make my own UIControl subclass -with a property of type NSMutableDictionary-, as superclass of all programmatically created UIElement s in anyway, so that I can reduce the number of subclasses.
Here like this
#interface UIControl : MyControls
{
}
#property(nonatomic,retain) NSMutableDictionary *details;
#end
and make MyControls as superclass of all programmatically created UIElements
You can do this, not by subclassing, but by adding your own properties and methods to UIControl itself (the superclass of UIButton, UILabel, etc.). These will then be inherited by any standard buttons, labels, etc. that you instantiate. Objective-C lets you add your own methods very easily using Categories. However, you can't add instance variables via categories. To do that, you need to use Associative References which are documented in the Objective-C Runtime Reference.
There's a good tutorial on how to do this here.
By the way, I don't necessarily disagree with inturbidus. But if you're sure you want to go this route, that's how you'd do it.