Translating #interface UIView from Objective-C to Swift - ios

I'm following an Objective-C tutorial on how to animate a menu with UIKit Dynamics and I'm having some trouble translating the following code from Objective-C to Swift.
animator.h
#interface Animator : NSObject
+ (instancetype)animatorWithScreen:(UIScreen *)screen;
- (void)addAnimation:(id<Animation>)animatable;
- (void)removeAnimation:(id<Animation>)animatable;
#end
#interface UIView (AnimatorAdditions)
- (Animator *)animator;
#end
animator.m
#implementation Animator
{
}
+ (instancetype)animatorWithScreen:(UIScreen *)screen
{
if (!screen) {
screen = [UIScreen mainScreen];
}
Animator *driver = objc_getAssociatedObject(screen, &ScreenAnimationDriverKey);
if (!driver) {
driver = [[self alloc] initWithScreen:screen];
objc_setAssociatedObject(screen, &ScreenAnimationDriverKey, driver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
return driver;
}
#implementation UIView (AnimatorAdditions)
- (Animator *)animator
{
return [Animator animatorWithScreen:self.window.screen];
}
#end
I was able to get everything else working, but I'm unsure how to get the UIView to have the animator property using Swift and also how to properly translate:
objc_setAssociatedObject(screen, &ScreenAnimationDriverKey, driver, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

There is already an existing discussion about objc_setAssociatedObject for swift: "Is there a way to set associated objects in Swift?".
What you are trying to do with the Animator is called an extension. That of course is available for swift as well - take a look at the docs for how to create one yourself.

Related

iOS Objective-C Get Reference on Currently Displayed UIAlertController

I wanted to get a reference on the currently displayed UIAlertController.
(Some guys thought my question sucks and close voted it. However they were wrong, as there IS an answer to the question and I will post it, so others interested in this can find it!)
I've made a singleton class with one weak reference, then I've extended UIAlertController and made a new method to present it, where I set this weak reference to the newly displayed alert. Now this will hold the reference of the alert as long as it has any other reference, thus while it is displayed.
UIAlertController+Extension.h
#interface UIAlertController(Extension)
- (UIAlertController*)showIn:(UIViewController*)viewController;
+ (UIAlertController*)lastOnScreenAlert;
#end
UIAlertController+Extension.m
#interface LastAlert : NSObject
#property (nonatomic, weak) UIAlertController* reference;
#end
static LastAlert* lastAlert;
#implementation LastAlert
+ (void)initialize
{
[super initialize];
lastAlert = [LastAlert new];
}
#end
#implementation UIAlertController(Extension)
- (UIAlertController*)showIn:(UIViewController*)viewController
{
[viewController presentViewController:self animated:YES completion:nil];
[LastAlert class];
lastAlert.reference = self;
return self;
}
+ (UIAlertController*)lastOnScreenAlert
{
return lastAlert.reference;
}
#end
There is a common solution to get the current alert controller that supports multiple windows and can work with third party libraries which present their own alerts:
static UIAlertController* currentAlertController() {
for (UIWindow* window in UIApplication.sharedApplication.windows) {
UIViewController* presented = window.rootViewController.presentedViewController;
while (presented != nil) {
if ([presented isKindOfClass:UIAlertController.class]) {
return (UIAlertController *)presented;
}
presented = presented.presentedViewController;
}
}
return nil;
}

Multi inheritance for IOS

I want create a class that can inherit from two custom class.
Do you have any idea to do this please?
Please see below my example:
first class:
#interface UIZoomableView : UIView
{
UITapGestureRecognizer *_tapGestureRecognizer;
}
and implementation:
- (void)onDoubleTap:(UITapGestureRecognizer *)sender
{
CGSize newSize;
CGPoint centerPoint = self.center;
if ([self isSmall])
{
newSize = [self bigSize];
}
else
{
newSize = [self smallSize];
}
[UIView animateWithDuration:0.3 animations:^{
self.size = newSize;
self.center = centerPoint;
}];
}
Second class:
#interface UIDraggableView : UIView
UIPanGestureRecognizer *_panGestureRecognizer;
#end
implementation:
- (void)handlePan:(UIPanGestureRecognizer*)sender
{
..
}
i want to create a custom view that can be zoomable and draggable.
Do you have any idea to do this please? (without copy code..)
I think something like protocols but i want default value for the base classes?
How can i implement this using protocol or something like protocols.
Thanks for any response!
Objective-C doesn't support multiple inheritance. You could use a protocol, composition and message forwarding to achieve the same result.
A protocol defines a set of methods that an object must implement (it's possible to have optional methods too). Composition is basically the technique of include a reference to another object and calling that object when it's functionality is required. Message forwarding is a mechanism that allows objects to pass messages onto other objects, for example, an object that is included via composition.
Apple Reference:
Protocols
Composition
Message Forwarding (and specifically Forwarding and Multiple Inheritance)
So, in your case Composition might be a solution, below is the example code
#interface ClassA : NSObject {
}
-(void)methodA;
#end
#interface ClassB : NSObject {
}
-(void)methodB;
#end
#interface MyClass : NSObject {
ClassA *a;
ClassB *b;
}
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB;
-(void)methodA;
-(void)methodB;
#end
#implementation MyClass
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB {
a = anA ;
b = aB ;
}
-(void)methodA {
[a methodA] ;
}
-(void)methodB {
[b methodB] ;
}
#end
If you don't want to implement all the methods from ClassA and ClassB in MyClass, you can use Message Forwarding in MyClass to handle all the method invocations. Below works fine as long as ClassA and ClassB do not have any common methods.
#implementation MyClass
-(id)initWithA:(ClassA *)anA b:(ClassB *)aB {
a = anA ;
b = aB ;
}
//This method will be called, when MyClass can not handle the method itself
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([a respondsToSelector:[anInvocation selector]])
[a invokeWithTarget:someOtherObject];
else if ([b respondsToSelector:[anInvocation selector]])
[b invokeWithTarget:someOtherObject];
else
[super forwardInvocation:anInvocation];
}
#end
The closest that you can get to multiple inheritance in Objective C is with categories. These are a mechanism for adding additional methods to a class that already exists.
Note that this has some important limitations:
You can't add properties or ivars using a category, though you can use associated objects to get a similar effect;
The compiler won't tell you if you have methods with the same name that are declared in the class and the category, or in two categories, so you have to be careful to avoid name collision;
This won't appear as a proper class (because Objective C does not have multiple inheritance) so you won't have something in your code called ScrollableZoomableView which inherits from ScrollableView and ZoomableView. That's not possible in Objective C (unlike C++ for example).
You need the -ObjC flag when linking files with categories, otherwise you'll get unrecognized selector errors when you run your code;
You can't get your code called during -init or +initialize, because those belong to the base class. You'll need to initialize your properties explicitly. You can still use +load though;
You can't intercept dealloc either, so you may need to be careful to explicitly deregister your listeners too.
You want something like this:
#interface UIView (Zoomable)
#property (nonatomic) UITapGestureRecognizer * my_tapGestureRecognizer;
#end
#implementation UIView (Zoomable)
-(void)my_enableZooming() {
self.my_tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(my_onDoubleTap:)];
self.my_tapGestureRecognizer.numberOfTapsRequired = 2;
[self addGestureRecognizer:self.my_tapGestureRecognizer];
}
-(void)my_disableZooming() {
[self removeGestureRecognizer:self.my_tapGestureRecognizer];
self.my_tapGestureRecognizer = nil;
}
-(void)my_onDoubleTap:(UITapGestureRecognizer *)sender {
...
}
-(UITapGestureRecognizer)my_tapGestureRecognizer {
return objc_getAssociatedObject(self, #selector(my_tapGestureRecognizer));
}
-(void)setMy_tapGestureRecognizer:(UITapGestureRecognizer)value {
objc_setAssociatedObject(self, #selector(my_tapGestureRecognizer), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end
#interface UIView (Draggable)
#property (nonatomic) UIPanGestureRecognizer * my_panGestureRecognizer;
#end
#implementation UIView (Draggable)
-(void)my_enableDragging() {
self.my_panGestureRecognizer = ...;
}
-(void)my_disableDragging() {
...
}
-(void)my_handlePan:(UIPanGestureRecognizer*)sender {
...
}
-(UIPanGestureRecognizer)my_panGestureRecognizer {
return objc_getAssociatedObject(self, #selector(my_panGestureRecognizer));
}
-(void)setMy_panGestureRecognizer:(UIPanGestureRecognizer)value {
objc_setAssociatedObject(self, #selector(my_panGestureRecognizer), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
#end

Obj-C Inheriting from SKSpriteNode

I have a class defined as
#interface Board : SKSpriteNode
+ (Board *) initWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (Board *) initWithScreenSize:(CGRect)screen
{
Board *board = (Board *) [SKSpriteNode spriteNodeWithImageNamed:#"turn4_board"];
return (Board *)board;
}
#end
There are other methods/properties but this is the relevant portion. My problem is that after initializing board (via the SKSpriteNode call), the variable is of type SKSpriteNode. I can't make it of type "Board", thus later run-time calls to an instance of "Board" cause a crash saying that my method doesn't exist in the class SKSpriteNode. I am apparently making a rookie mistake, but I'm starting to go bald over this one. Please provide the requisite smack upside my head, and then please follow up with a helpful fix/suggestion. Thanks!
I should, perhaps, mention that I'm using Xcode 5.1.1 developing for iOS 7 under MacOS 10.9.4.
Because you created a SKSpriteNode not Board. Cast fool the compiler but it won't make it working.
#interface Board : SKSpriteNode
+ (instancetype) boardWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (instancetype) boardWithScreenSize:(CGRect)screen
{
return [self spriteNodeWithImageNamed:#"turn4_board"];
}
#end
You need to create a proper init method and conventionally a convenience constructor will start with boardWith...:
#interface Board : SKSpriteNode
+ (Board *)boardWithScreenSize:(CGRect)screen;
- (instancetype)initWithScreenSize:(CGRect)screen;
#end
#implementation Board
+ (Board *)boardWithScreenSize:(CGRect)screen
{
return [[Board alloc] initWithScreenSize:screen];
}
- (instancetype)initWithScreenSize:(CGRect)screen
{
self = [super initWithImageNamed:#"turn4_board"];
if (self) {
// Additional init
}
return self;
}
#end

Mixins or Multiple Inheritance in Objective-C?

Let's say for example that I have MyUITextViewSubclass which inherits from UITextView and MyUITextFieldSubclass which inherits from UITextField and both of those subclasses contain a lot of the same methods and properties to add similar behavior to those UI controls.
Since UITextView and UITextField inherit from different classes, is there an easy way to create an abstract class to combine all of that repeated code? In other words, is it possible to create an abstract class that I could inherit from for both of those subclasses and then just override the methods that are different between the two?
What I know so far:
I know Objective-C doesn't support multiple inheritance (inheritance from two or more classes)
I know I could add common methods using Categories, but I don't think that solves overriding init methods or adding private properties
Building on Amin's answer, this is how you could do it:
Step 1: Create a TextSurrogateHosting protocol that will contain all the methods of your UITextField and UITextView subclasses that you need to access from the methods that you want to add to both subclasses. This might for example be a text and setText: method, so that your methods can access and set the text of either a text field or a text view. It might look like this:
SPWKTextSurrogateHosting.h
#import <Foundation/Foundation.h>
#protocol SPWKTextSurrogateHosting <NSObject>
- (NSString *)text;
- (void)setText:(NSString *)text;
#end
Step 2: Create a TextSurrogate class that contains all the methods that you want to share between both the UITextField and the UITextView subclasses. Add those methods to a protocol so that we can use code completion in Xcode and avoid compiler warnings/errors.
SPWKTextSurrogate.h
#import <Foundation/Foundation.h>
#import "SPWKTextSurrogateHosting.h"
#protocol SPWKTextSurrogating <NSObject>
#optional
- (void)appendQuestionMark;
- (void)appendWord:(NSString *)aWord;
- (NSInteger)characterCount;
- (void)capitalize;
#end
#interface SPWKTextSurrogate : NSObject <SPWKTextSurrogating>
/* We need to init with a "host", either a UITextField or UITextView subclass */
- (id)initWithHost:(id<SPWKTextSurrogateHosting>)aHost;
#end
SPWKTextSurrogate.m
#import "SPWKTextSurrogate.h"
#implementation SPWKTextSurrogate {
id<SPWKTextSurrogateHosting> _host;
}
- (id)initWithHost:(id<SPWKTextSurrogateHosting>)aHost
{
self = [super init];
if (self) {
_host = aHost;
}
return self;
}
- (void)appendQuestionMark
{
_host.text = [_host.text stringByAppendingString:#"?"];
}
- (void)appendWord:(NSString *)aWord
{
_host.text = [NSString stringWithFormat:#"%# %#", _host.text, aWord];
}
- (NSInteger)characterCount
{
return [_host.text length];
}
- (void)capitalize
{
_host.text = [_host.text capitalizedString];
}
#end
Step 3: Create your UITextField subclass. It will contain three necessary boilerplate methods to forward unrecognized method invocations to your SPWKTextSurrogate.
SPWKTextField.h
#import <UIKit/UIKit.h>
#import "SPWKTextSurrogateHosting.h"
#import "SPWKTextSurrogate.h"
#interface SPWKTextField : UITextField <SPWKTextSurrogateHosting, SPWKTextSurrogating>
#end
SPWKTextField.m
#import "SPWKTextField.h"
#implementation SPWKTextField {
SPWKTextSurrogate *_surrogate;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_surrogate = [[SPWKTextSurrogate alloc] initWithHost:self];
}
return self;
}
#pragma mark Invocation Forwarding
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([_surrogate respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:_surrogate];
} else {
[super forwardInvocation:anInvocation];
}
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [_surrogate methodSignatureForSelector:selector];
}
return signature;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector] ||
[_surrogate respondsToSelector:aSelector])
{
return YES;
}
return NO;
}
#end
Step 4: Create your UITextView subclass.
SPWKTextView.h
#import <UIKit/UIKit.h>
#import "SPWKTextSurrogateHosting.h"
#import "SPWKTextSurrogate.h"
#interface SPWKTextView : UITextView <SPWKTextSurrogateHosting, SPWKTextSurrogating>
#end
SPWKTextView.m
#import "SPWKTextView.h"
#import "SPWKTextSurrogate.h"
#implementation SPWKTextView {
SPWKTextSurrogate *_surrogate;
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_surrogate = [[SPWKTextSurrogate alloc] initWithHost:self];
}
return self;
}
#pragma mark Invocation Forwarding
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
if ([_surrogate respondsToSelector:[anInvocation selector]]) {
[anInvocation invokeWithTarget:_surrogate];
} else {
[super forwardInvocation:anInvocation];
}
}
- (NSMethodSignature*)methodSignatureForSelector:(SEL)selector
{
NSMethodSignature* signature = [super methodSignatureForSelector:selector];
if (!signature) {
signature = [_surrogate methodSignatureForSelector:selector];
}
return signature;
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
if ([super respondsToSelector:aSelector] ||
[_surrogate respondsToSelector:aSelector])
{
return YES;
}
return NO;
}
#end
Step 5: Use it:
SPWKTextField *textField = [[SPWKTextField alloc] initWithFrame:CGRectZero];
SPWKTextView *textView = [[SPWKTextView alloc] initWithFrame:CGRectZero];
textField.text = #"The green fields";
textView.text = #"What a wonderful view";
[textField capitalize];
[textField appendWord:#"are"];
[textField appendWord:#"green"];
[textField appendQuestionMark];
NSLog(#"textField.text: %#", textField.text);
// Output: The Green Fields are green?
[textView capitalize];
[textView appendWord:#"this"];
[textView appendWord:#"is"];
NSLog(#"textView.text: %#", textView.text);
// Output: What A Wonderful View this is
This pattern should solve your problem. Hopefully :)
Some more background information is available here: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/ObjCRuntimeGuide/Articles/ocrtForwarding.html#//apple_ref/doc/uid/TP40008048-CH105
What you want is a mixin. This is not supported in Objective-C. Categories are no mixins, because they add an api to one class not to many (>1) classes. Using categories, what is not possible for many reasons as you said, would not help you.
The usual way to solve that problem is to create a helper class containing the additional code and use it in both classes.
Then you will find yourself typing
[myUITextViewSubclass.helper doSomething]
instead of
[myUITextViewSubclass doSomething]
If this is really a problem, you can solve this with forward invocations. Just write a comment.
No. It is not possible.
The closest thing you could achieve would be to manually add functionality to UITextView to make it mimic UITextField. The obvious downside is that you must do this all manually, with your own code.
You could use a preprocessor macro, but that is error-prone.
Traits or Mixins are not supported by Objective-C, you only have built-in option of Categories.
But fortunately Objective-C Runtime has almost all tools for implementing own idea if mixing or traits with adding methods and properties to your class at runtime. You can read more about opportunities which Objective-C Runtime provides for you on Apple's documentation website Objective-C Runtime Docs
The idea is:
1) You can create an Objective-C protocol (Mixin), in which you will declare properties and methods.
2) Then you create a class (Mixin implementation), which will implement methods from this protocol.
3) You make your some class, in which you want to provide the possibility of composition with mixins, to conform that protocol (Mixin).
4) When your application launches, you add with Objective-C runtime all implementations from (Mixin implementation) class and properties declared in (Mixin) into your class.
5) voilĂ  :)
Or you can use some ready open source projects such as "Alchemiq"

Manipulating properties in init that are set up by a sub-class in Objective-C

I have an abstract interface in Objective-C where every sub-class needs to set up a property and then do the exact same thing with that property at the end of init. I'm trying to avoid duplicated code with something like this:
Interface File
#interface Shape : NSObject
#property (nonatomic) PropertyType *prop;
- (id)init;
- (void)initProperty;
#end
Implementation File
#implementation Shape
- (id)init
{
if(self = [super init]) {
[self initProperty];
[prop doSomething];
}
return self;
}
- (void)initProperty
{
}
#end
My problem is that every sub-class will need a different set of parameters passed to initProperty in order to implement the method correctly:
#implementation Rectangle
- (void)initPropertyWithRect:(CGRect)rect
{
prop = [RectangleStuff rectangleWithRect:rect];
}
#end
#implementation Circle
- (void)initPropertyWithRadius:(CGFloat)radius
{
prop = [CircleStuff circleWithRadius:radius];
}
#end
Is there a clean way to do what I'm trying to do in Objective-C? So far, my options seem to be:
Create a "property bag", and just pass around an NSDictionary.
Duplicate the [property doSomething]; code in every subclass.
Somehow pass in a factory object to init, and have the factory object create prop. This approach seems the cleanest, but I'd need the factory object to keep the rect and/or radius as internal state somehow, and that doesn't seem clean to me.
Any thoughts?
I would probably choose #2 (to keep it simple). If the property is only set once
(in the subclass init method), you could override the property setter method in the
superclass, and do the additional stuff there.
Untested code:
- (void)setProp:(PropertyType *)prop
{
_prop = prop; // (Assuming ARC)
[_prop doSomething];
}
First, I feel obligated to mention that your init function should not do anything besides initialize the object. That said, every rule has a time and a place to be broken, so I'll offer what suggestions I can.
Your init function is no different than any other function. You can do things before and after you call super. While generally discouraged, this would be a good place to do it. Your init in your subclass would now look like this:
- (id)init
{
self.myProperty = value;
self = [super init];
if (self) {
// more init stuff
}
return self;
}
I ended up using a variant of what was suggested in the other two answers:
Shape.h
#interface Shape : NSObject
#property (nonatomic) PropertyType *prop;
- (id)initWithProperty:(PropertyType *prop);
#end
Shape.m
#implementation Shape
- (id)initWithProperty:(PropertyType *)prop
{
if(self = [super init]) {
_prop = prop;
[_prop doSomething];
}
return self;
}
#end
Rectangle.m/Circle.m
#implementation Rectangle
- (void)initWithRect:(CGRect)rect
{
return [self initWithProperty:[RectangleStuff rectangleWithRect:rect]];
}
#end
#implementation Circle
- (void)initWithRadius:(CGFloat)radius
{
return [self initWithProperty:[CircleStuff circleWithRadius:radius]];
}
#end

Resources