I want to implement a movable UIView class (view moves when you touch on it and move your finger), e.g.:
#interface MovableView : UIView {
some members;
}
-touchesBegan;
-touchesMoved;
#end
How can I apply this code to UILabel, UIButton etc without multiple inheritance? In C++, I'd do it like this (with UIView as a virtual base to MovableView):
struct MovableLabel : UILabel, MovableView {};
Categories are offered as an alternative to multiple inheritance but I definitely don't want to extent UIView using categories because that would apply to all views in my application. I only want some of my views to be movable.
My current strategy to avoid code duplication is putting MovableView code in a header file and including it everytime I need some class to use it. Besides that I have to add members of MovableView to whatever class I am writing. It is pure code ugliness but better than copy/pasting code all over my project.
Does anyone have any other ideas to get around this Objective C limitation [of not having multiple inheritance]?
Thanks,
Altan
Objective-C won't let you do this mixin inheritance thing, except by adding the same category to each of the classes you care to augment. And then, you won't get extra instance variables.
For your specific problem, I might approach it by designing MovableView as a container class that was just an object that has a child view (UILabel, UIButton, etc) that you want to move as its subview.
Categories would not help anyway because replacing the real touchesBegan method on UIButton would be pretty bad... you can't call [super] from a category.
Another solution would be to inject a method into those class definitions with something like:
Method origMethod = class_getClassMethod([UIButton class], #selector(touchesBegan));
Method newMethod = class_getClassMethod([TemplateClass class], #selector(replacementTouchesBegan));
method_exchangeImplementations(origMethod, newMethod);
But that's pretty fiddly. Although if it's really where you want to go, it's worth looking into...
What I would do is make a MovableView class that inherits from UIView, and simply add whatever you want movable as a subview of that view (which I think might be slightly different than what quixoto was saying, my apologies if not). It gets to respond to touches first and passes along whatever it doesn't need to the next responder... no need to build up a special class with a single subview. Then in IB you can just place these movable views wherever and put things inside of them.
In general compositional techniques like this are very handy across UIKit rather than modifying core classes.
Related
This question seems to have been asked several times, but I have yet to find an definitive answer. I'm totally aware of the fact that NSCell is "being gradually phased out". This is great (and long overdue!), but it doesn't help in the here and now. Suppose I want to create a subclass of NSImageView which (e.g.) clips an image with an arbitraily shaped mask. I might define it like this:
class MaskedImageView : NSImageView {
public override class var cellClass: AnyClass? {
get { MaskedImageCell.self }
set {}
}
MaskedImageCell, of course, inherits from NSImageCell. This code compiles just fine under Swift 5.x, but the cell class is never changed. Adding the statement super.cellClass = newValue to the above property setter likewise makes no difference. (In fact, this isn't how the underlying AppKit machinery establishes the cell class. If I add a print statement inside the property getter, I can see that AppKit is interrogating the cell class, as I'd expect, but it never uses it.)
Like I said, I'm aware that NSCell is on the way out. But there are surely multiple use cases where it would be useful to change the cellclass to add specifics required by a particular application. If this isn't possible NOW, then is one supposed to try and replicate the entire functionality of (eg) NSImageCell. I surely wouldn't want to do that!
All constructive comments gratefully received. And please do bear in mind that I'm not actually interested in adding weird-shaped masks to NSImageViews. 😄 What I'm after is a definitive solution to the problem of introducing one's own cell class into the mix. We were told 7 years ago that NSCell was being deprecated, but while it's still with us, we should have a mechanism for introducing custom cell classes.
I've defined two classes in an m file, the first subclassing UIView and the second UIViewController. The UIViewController is instantiated at some point, and the vc is who instantiates my first class.
the first class implements the touchesEnded method, to simulate a button. when the touchesEnded method is fired in the first class, is it possible to easily call a method defined in the 2nd class, without going into delegates and such?
I tried playing with selectors with no luck
is it possible to easily call a method defined in the 2nd class
Yes, assuming that you are creating an instance of the second class and calling the method on that instance.
Regardless of whether the two classes are subclasses of the same type, or in the same or different files, you need a reference to an instance of that class to call a method on it, or force it to perform a selector.
The proper OO way to do this is with delegates, but you could theoretically do something like pass a reference to view 2 into view 1 when you create the views. If you create them in IB you could create outlets so they reference each other that way.
In short: Yes, it is possible and easy to do, but I can't give you too much in terms of specific code without a more specific example of your situation
If I am using a custom UITableViewCell I can use the following methods to change the cell's appearance when transitioning state:
- (void)willTransitionToState:(UITableViewCellStateMask)state
- (void)didTransitionToState:(UITableViewCellStateMask)state
Is there a way to achieve this if I'm not using a custom tableview cell?
Edit: Please see Daniel Hanly's comment. Categories may be selectively applied using #import. My apologies to anyone that may have been misled by this answer. Relevant sections will be redacted for future reference.
Okay, attempt number two. As far as I am aware, there is no other documented way to implement the functionality you require without subclassing UITableViewCell. It's worth noting that Apple's docs on UITableViewCell specifically mention that the state transition methods are meant to be implemented by subclasses. That having been said, If you absolutely need to implement them without a subclass, there are a couple of less conventional solutions. Each comes with its own issues, and it may end up being unfeasible for you to implement them, but it's an interesting question nonetheless.
Disclaimer
If you only want a sane and relatively simple explanation, then consider the answer to your question to be "no, there is no way to do what you want." I only present the options below with the assertion that they will work. In no way do I endorse actually using them. Consider this my penance for providing my first answer with such an obvious flaw.
Option One - Categories
It is possible to get the functionality you're looking for by overriding the methods you listed in a custom UITableViewCell category.
The problem is that this approach would be a pretty bad idea 99% of the time. Once you define the category on UITableViewCell, those methods would be defined for all UITableViewCell objects throughout the app. Unless you want the exact same state transition functionality for every single table cell in the app, this approach isn't very helpful.
Option Two - Runtime magic
You can use the low-level Objective-C runtime functions to change the implementation of any method on the fly. Unlike the categories option, this approach is flexible enough to redefine the intended behavior whenever you need to, instead of being a one-shot deal.
For example, if you're trying to manage state transitions from a UITableViewController, you could do this:
CustomTableViewController.m
#import <objc/runtime.h>
- (void) customStateWillChange:(UITableViewCellStateMask)state
{
//custom UITableViewCell code
}
- (void) viewDidAppear:(BOOL)animated
{
//Store the original implementation
Method originalStateWillChangeMethod = class_getInstanceMethod([UITableViewCell class], #selector(willTransitionToState:));
originalStateWillChangeImplementation = method_getImplementation(originalStateWillChangeMethod); //variable declared in header file as type IMP
//Get the new implementation
Method newStateWillChangeMethod = class_getInstanceMethod([self class], #selector(customStateWillChange:));
IMP newStateWillChangeImplementation = method_getImplementation(newStateWillChangeMethod);
//Replace implementation
method_setImplementation(originalStateWillChangeMethod, newStateWillChangeImplementation);
//the rest of your viewDidAppear code
[super viewDidAppear:animated];
}
- (void) viewDidDisappear:(BOOL)animated
{
//restore the original implementation
Method originalStateWillChangeMethod = class_getInstanceMethod([UITableViewCell class], #selector(willTransitionToState:));
method_setImplementation(originalStateWillChangeMethod, originalStateWillChangeImplementation);
//rest of viewDidDisappear code
[super viewDidDisappear:animated];
}
This code may not suit your exact purposes, but I think it provides a useful example.
It's incredibly ugly though because the customStateWillChange: method defined here is only intended to be run as a part of the UITableViewCell class, but in this example it will be compiled as though it were part of the CustomTableController class. Among other annoyances, you would have to eschew the property dot notation, ignore compiler warnings and give up most if not all compile-time checks for that method's body.
Option 3 - Category with runtime magic
Exactly what it sounds like. Define any custom state change methods you like within a category (or several categories) on UITableViewCell. Be sure that each one has a separate name - adding two categories that each have a method of the same name will result in undefined behavior. Also, each one needs to have the same return type and argument types as the method it is intended to replace.
Then the references to [self class] in the above code would be replaced with [UITableViewCell class], and the customStateWillChange: method would be moved to the custom category. While still ugly, you can at least rely on the compiler to interpret the method bodies properly.
Of course, messing with the runtime adds a whole lot of complexity to keep track of. It could work fine, but it's not good design, it would take serious effort to ensure it worked safely and correctly, and it would be likely to bring anguish and despair to anyone maintaining it.
References
The Objective-C Programming Language - Categories and Extensions
Objective-C Runtime Reference
Absolutely. The UITableViewDelegate protocol specifies a number of methods to manage state transitions for the table view's cells. Take a look at the UITableViewDelegate Class Reference, specifically the methods listed under the heading "Editing Table Rows".
Edit
Sorry, you're right. The UITableViewDelegate methods don't respond to direct changes to the cell's properties. I've found a way that does work, but I'm going to put it in a different answer to avoid confusion.
This may be a silly question but I haven't found any information on it.
Let's say several of the classes in my program derive from 'MySubView' which is derived from another class, UIViewController.
I would declare it like this:
#interface NewViewController : MySubView {
// code ...
}
#end
In the future the client wants a change, and desires another view with a table. So I would need to make another class, called MySubTableView, that is a UITableViewController subclassed from MySubView.
I was thinking this would be easier if I could do something like this:
#interface NewViewController : UITableViewController : MySubView {
// code ...
}
#end
But this doesn't work.
Is there a way to do this with Xcode, or do I have to specifically make the class itself?
EDIT:
I'm not looking for multiple inheritance. A straight inheritance hierarchy would follow:
NewViewController
UITableviewController
MySubView
UIViewController
No, Objective-C doesn't support declaring those kind of (vertical) inheritance chains. You can only specify the direct super class.
Even if it was possible, there would be problems like calling the correct initializers as they won't be called automatically. Consider a hierarchy like A : B : C - now you can initialize B using e.g. [super init] in As initializer, but how would B know what initializer you want it to call for C?
Objective-C doesn't support multiple inheritance... But Objective-C programmers rarely miss it, because you can accomplish many of the same tasks using Categories instead. Read up on Objective-C Categories.
I'm making a game for the iPhone, and I have a class called Robot.
Then I have a class called View, which renders everything.
I want to send a copy of my Robot, which I defined in my ViewController, and I send it to gameView (which is View *gameView), like this:
robot = [Robot new];
[gameView setRobot: [robot copy]];
I tried to make a copy but that didn't work, I could also do it with a pointer to Robot (&robot) but sometimes it just crashes ?
I tried this in my View.h #interface definition:
#property (copy) Robot* robot;
but I get the error
/RobotsAdventure/Classes/View.h:24: error: setter '-robot' argument type does not match property type
:/
Help? I'm pretty new at this, heh.
In order to copy an object, its class has to implement the NSCopying Protocol.
See Memory Management Programming Guide for Cocoa
The simplest way to accomplish what you want is:
View.h
#import "Robot.h"
#property(nonatomic,retain) Robot *robot.
Then set it like so in the view controller:
// myView is of class View.h
myView.robot=self.robot;
By the way, naming a class "View" is simply asking for a naming collision at some point. Worse, you'll never remember what the class does months down the road when you come back to it. Neither will you be able to search for it. Instead use a highly descriptive name e.g. RobotDisplayView_MyProjectName. With autocomplete it doesn't take any longer to type out than a short name.