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.
Related
I have a class that defines all styles on a UIVIew.
They are all predefined but I'm not sure when to fire this.
When I try to create an extension for this:
extension UIView
{
func willMoveToSuperview(newSuperview: UIView?)
{
self.stylize() // Another extension somewhere (not here my problem)
}
}
And I'm getting this error:
Method 'willMoveToSuperview' with Objective-C selector conflicts with
previews declaration with the same Objective-c selector
I have tried to override it, but didn't worked either.
Any ideas on how to be able to apply a same behaviour when all of my UIViews will become visible?
You can use Swizzling technic to customize UIView's function. Take a look at:
http://nshipster.com/method-swizzling/ (objective-c)
or
http://nshipster.com/swift-objc-runtime/ (swift)
Hope that helps.
Even though Swift's Extensions are similar to Categories from Objective-C, what you are trying to do is not allowed in Swift.
You cannot override existing functionality:
Extensions can add new functionality to a type, but they cannot override existing functionality.
Source: Swift Extensions - Apple Documentation
Depending on what it is that you are trying to style, you might want to take a look at UIAppearance, it will allow you to style default colors for the UINavigationBar, amongst other things. NSHipster has a good post about it: NSHipster - UIAppearance
You can create a subclass of UIView with the method .stylize().
Then each view you create, you inherit of you UIView subclass.
You'll be able to cal .stylize() on each UIViewSubclass. Simply write the style code inside the subclass and inherite.
Or
Use a category to add the method to the existing UIView class.
See : https://developer.apple.com/library/ios/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/CustomizingExistingClasses/CustomizingExistingClasses.html
Outside of swizzling (not generally recommended), or subclassing as noted by David in his answer, there isn't really a way to override existing methods on a class and its subclasses.
One thing you might try is creating a base class for your view controller instead of all your views. In your view controller base class, you could override viewWillLayoutSubviews to recurse through the view hierarchy and call stylize on each view. This means you would be using the subclass approach in fewer places (just view controllers as opposed to all views).
Another thing you might consider if taking the subclassing approach with UIView is that if you are subclassing anyway, you can take advantage of things like #IBDesignable and #IBInspectable to better integrate those UIView subclasses with storyboards and live preview.
I wrote a Swift library which does exactly this, and it works well for the type of styling it seems you want to do: https://github.com/daniel-hall/Stylish
I have a case where I need to modify all instances of NSView, trivial by adding a couple of lines of code to init() but how can that be done cleanly for all subclasses in Swift?
The case I have in mind:
I add a custom property to NSView via an extension
That works fine, but isn't animatable until it's registered as animatable via the layer's addAnimation:forKey: method.
So all I really need to do is ensure that addAnimation:forKey: gets called every time NSView or a subclass gets called.
Things I've considered:
Somehow doing this in an extension. Problem: extensions can't override functions, and without overriding init() or something like viewDidAppear() the code has to be called manually every time (e.g. with a convenience initialiser).
Subclass NSView and add the code. Problem: NSButton, Slider and all the rest won't inherit the code, and I have to subclass them all.
Method swizzling. This should be possible, because obj-c methods are swizzlable and NSView inherits from NSObject.
So far only swizzling seems viable, but I'm reluctant to do this because it's reliant on the APIs staying obj-c friendly, and I don't know how long that will be the case for.
Is there another way?
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.
I am going to have several uiimageview interactive sulasses that need a delegate. when each one of them is touched, the view controller must respond by changing something.
Is it good common practice to have one method , that is the delegate method get called by all of the uiimageview subclass instances?
The delegate will be the view controller.
Overall, is that good practice?
One method is better. This one will be a good option - (void)imageViewTouched:(UIImageView *)sender
You should follow DRY (Do not Repeat Yourself) principle and eliminate code duplcication in your app. So you should have separate methods if they have separate logic. You should have one method if the same type of logic executed for various senders.
Absolutely. Pass in the sender so you can make decisions based on who sent you the message (like tableView:cellForRowAtIndexPath:), and you have a good implementation of the delegation pattern.
I am learning to program the iphone and I wanted to do some drawing. I followed some example code and subclassed the viewcontroller and it worked fine. Now as I wanted to expand the program I came upon a design question that I could use a little help on.
I subclass myviewcontroller with mynewview. If I have any code in the myviewcontroller how do I call or reference it in mynewview and vice versa? I am not sure if I am asking this right but I am trying to understand the relationship between the class and subclass.
Objective-C objects benefit from inheritance. All classes are subclasses of NSObject, therefore you can call init on any object. If you created a custom class and gave it a method doSomethingAwesome, you are free to then implement doSomethingAwesome in any subclass of your custom class. However, declaring a method in a subclass does not add that method to the superclass. As an aside, I rarely find myself subclass sing my own custom classes. I believe that it is encouraged to maintain what is called a shallow object hierarchy. Usually I subclass the stock cocoa classes, customize to my needs and if I need custom methods in more than one subclass I will declare a category on the superclass rather than relying on inheritance to provide my custom behavior
The messaging system in Objective-C is dynamic. Every object includes a struct with information that the runtime use for introspection. Here the runtime will find a list of methods the object is able to respond. So, let's say you message an instance like this:
[mynewview someMethod];
The runtime will first check the object information to trying to find some method that will be able to respond the message. If nothing is found, then will query the super class, and so on. In fact, the runtime is much more complex, and will give any object more opportunities to respond (that's the dynamic part. For instance, mynewview might not have any method called someMethod and yet, might be able to satisfy the call, but that's something you might not want to worry right now).
From a child class you can call the superclass implementation of a given method with the keyboard super, so if mynewview is a subclass of myviewcontroller you can call myviewcontroller implementation from mynewview with:
[super someMethod];
If someMethod is both present in myviewcontroller and in mynewview, the runtime will automatically only call the child implementation, you have to call the parent implementation (if you have to) from the child implementation.