I think of myself as an experienced Objective-C programmer. I do apps for a living, and use the language features to it's fullest. That includes using the runtime for varies just-in-time changes to existing frameworks. Stuff like method swizzling and dynamic subclassing really shows how this language is so much more versatile than it's other object oriented C counterpart.
But lately I've been having some thoughts about an old feature, that I still find myself in need of using from time to time, but has been deprecated for some time now. It's a replacement for the old class_setSuperClass.
I often find myself subclassing UIKit classes in order to extend them or just change their behaviour slightly. One example I came across recently was an UIScrollView subclass, that made some conditions to the panGestureRecognizer. I did that by subclassing UIScrollView and then implementing the gestureRecognizer:shouldBeRequiredToFailByGestureRecognizer:.
This had to be done this way, because you're not allowed to change the delegate of a scroll view's pan gesture recogniser (it will throw a runtime exception), and you cannot create your own custom UIScrollView panGestureRecognizer.
Now this all worked out great. I changed all usages of UIScrollView in my app to my newly created subclass, and everything worked as expected. But what about UITableView and UICollectionView which I also used throughout the app. What to do with those? As you know, both of these classes is inherited from UIScrollView, but not my custom subclass of UIScrollView. So I found myself ending up writing the same code multiple times for every class that existed (and was used in the app) that inherited from UIScrollView. Writing the same code multiple times is a programmers 1-2-3 no-go. But I did end up writing a custom subclass not only for UIScrollView but also for UITableView and UICollectionView.
Now in the old days with the class_setSuperClass you were able to "swizzle" the superclass of an already prepackaged class like UITableView. You basically just said class_setSuperClass([UITableView class], [MyScrollView class]), and everything worked out (well almost) fine. Now you had injected your own class in between UITableView and UIScrollView. So every time you did [[UITableView alloc] init] it would have the features of MyScrollView because it inherited directly from it.
Now class_setSuperClass was deprecated in iOS 2.0! Since that we could do some swizzling and hacking with the object's isa pointer. Now that has been deprecated too.
So my question is really simple. How would you approach this?
If you want to globally change a class's behavior, rather than play with class identities, the direct approach would just be to replace the method you want replaced. The runtime still allows this, with class_replaceMethod(). That seems like the simplest way to accomplish what you want here.
Related
Some of my UITextFields are inherited from KaedeTextField from TextFieldEffects
I want to make these textfields to have masks from another framework, the AKMaskField
How could that be done? How can I make a textfield have the style of the KaedeTextField but at the same time also have the masks from AKMaskField?
is it possible to do that in Swift using protocols and extensions?
Swift does not support multiple inheritance for classes. Additionally you could conform to a protocol, but since they are both subclasses of the UITextField, there is no reasonable way and frankly - it doesn't make any sense, since they both handle it their own way.
You would (most likely) end up with a subclass of the first library and then you could recreate a missing feature from the second one.
How would I go about getting my extension to observe a property on the class it's extending while also being able to remove it at a later time?
For example:
I have a UIView, and I'd like to listen for frame changes within my UIView extension. When these changes occur I need to run some code that alters the views appearance based on the new frame.
The problem I'm having is, that while I can set up an observer, I can't figure out a way to remove it. There's no deinit() and I'd like the observer to hang around for the lifecycle of the UIView.
I'd rather not have to put this removal responsibility on the developer, and I don't want to subclass.
It doesn't even have to be KVO, if there's a better way I'd love to know about it.
The usual solution to this is to use an associated object as a deallocation spy. Associated objects are released when their attached object is deallocated, so you can run code in their deinit that you want to fire when the attached object goes away. That said, doing this really well in a multi-threaded world is a little tricky to say the least. I don't recommend building your own. Instead, I suggest PMKVObserver which will handle this for you. Even if you do choose to build your own, you should study how PMKVObserver does its magic so you're not overly naïve in your implementation.
Doing this sloppily (not worrying about multi-threaded race conditions) is not too hard in Swift, but to do it really well is better done in ObjC and bridged to Swift. Again, consult the code for the tricky corner cases.
While
Extensions can add new convenience initializers to a class, but they
cannot add new designated initializers or deinitializers to a class.
Designated initializers and deinitializers must always be provided by
the original class implementation.
So I think you cannot handle it in any nice way without subclassing.
For analytic purposes, I would like to do the following:
When a UIVIewController's viewDidLoad() is triggered, I would like to trigger a custom function vcWasLoaded(vc:UIViewController).
Similarly, when a UIButton is tapped, I would like to trigger a custom function btnWasTapped(bt:UIButton).
I would like to achieve the above without subclasses. Anyway to achieve the above using protocols, extensions, or reactive frameworks?
Method swizzling is the only thing I can think of that you could use that would let you do this without subclassing. You'd replace the implementation of viewDidLoad, and one of the lower-level button methods, and then call the original implementation in yours. (I've only dabbled in method swizzling, and it was many years ago, before Swift existed. I don't know much about Objective-C method swizzling, and know exactly zero about method swizzling in Swift.)
This would be much simpler and cleaner if you created a subclass of UIViewController and made it the base class of all of your view controllers.
Is there any possible way to detect every change on User Interface during runtime??
I'm trying to find all objects in the current app interface.
I'm trying to to get all nodes inspecting recursively the main Window, but, for example, how to know if the top viewcontroller changes or if it's added a uiview dynamically, or is presented a modalview??
The main objective is to have a library to do this..
Any idea, help?
Thanks!
You could write your own library based on this, using advanced Objective-C techniques. I do not recommend you to do this, since it mostly breaks MVC patterns on iOS. Depends on what do you want to use it for, maybe analytics?
So these are the options I believe, if you want to actively inspect UIView hierarchy. All options are pretty complicated though.
Swizzle methods such as addSubview and removeFromSuperview of UIView, so you could know when changes like that happens. Including the getters of frame and bounds, if you wish to know the position.
You could use KVO to watch properties such as: subviews, frame, bounds, superview to notice any changes. But at one point you would have to add the same object as the observer (could be singleton).
Decide for an interval that is fired by a NSTimer and go through the hierarchy recursively beginning at keyWindow on UIApplication. This would have a big performance impact though.
There may be other options, but these are the ones I believe to be the best choices.
I am just starting out with iOS app development and it's been a great experience so far. Apple documentation is great, but there are some questions I have that are not as technical and only someone with experience might be able to answer.
I have a bunch of UIViewController which handle the "dynamic skinning" of the custom UIViews that they control. This leads to a controller with big chunks of code which seems a bit unpractical to me.
So the question is: Following the MVC pattern, should I give the responsibility of setting a UIFont, UIColor, etc to the view itself? Or should I create "micro" controllers that handle this task using some kind of input?
Thanks for the response.
Creating UIView subclasses that handle the layout works. Override layoutSubviews in the UIView subclass to do the positioning layout (setting frames etc). I find the init method to be a good place to set fonts, colors etc.
Now the UIViewController has relatively little code related to the custom UIView. The viewController just needs to position an instance of the custom UIView and perhaps set a few properties (like a textLabel's text).