The class below is attempting to stop any implicit animations from occurring when a CALayer has a property changed.
// NoImplicitAnimations.h
#import <Foundation/Foundation.h>
#import <QuartzCore/QuartzCore.h>
#interface NoImplicitAnimations : NSObject
- (id<CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)key;
#end
// NoImplicitAnimations.m
#import "NoImplicitAnimations.h"
#implementation NoImplicitAnimations
- (id<CAAction>) actionForLayer:(CALayer *)layer forKey:(NSString *)key {
return (id)[NSNull null];
}
#end
I import NoImplicitAnimations.h in my Objective-C to Swift bridging header.
I create a global constant let _noImplicitAnimations = NoImplicitAnimations().
I extend the CALayer class like so:
extension CALayer {
func noImplicitAnimations () {
delegate = _noImplicitAnimations
}
}
Now comes the problem. I use myLayer.noImplicitAnimations() right after I create myLayer. Yet, implicit animations are still happening.
What am I doing wrong here?
Nevermind. This actually does work. I was testing it on the wrong CALayer. My bad!
Related
I need to create proxy pattern in iOS using swift
I have tried it using Objective C and here is the code
MyProtocol.h
#import <Foundation/Foundation.h>
#protocol MyProtocol <NSObject>
#required
-(void)testMessage;
#end
TestBO.h
#import <Foundation/Foundation.h>
#import "MyProtocol.h"
#interface TestBO : NSObject <MyProtocol>
#end
TestBO.m
#import "TestBO.h"
#implementation TestBO
-(void)testMessage{
NSLog(#"Test Message");
}
#end
TestProxyHandler.h
#import <Foundation/Foundation.h>
#interface TestProxyHandler : NSProxy
#property (nonatomic, strong) id object;
- (instancetype)initWithProtocol:(Protocol *)protocol andObject:(Class)clazz;
- (void)forwardInvocation:(NSInvocation *)invocation;
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector;
#end
TestProxyHandler.m
#import "TestProxyHandler.h"
#import "TestBO.h"
#implementation TestProxyHandler
- (instancetype)initWithProtocol:(Protocol *)protocol andObject:(Class)clazz{
if ([clazz conformsToProtocol:#protocol(MyProtocol)]) {
self.object = [[clazz alloc] init];
}else{
NSLog(#"Error it does not conform to protocol");
}
return self;
}
- (void)forwardInvocation:(NSInvocation *)invocation{
NSString *selString = NSStringFromSelector(invocation.selector);
NSLog(#"Called %#",selString);
[invocation invokeWithTarget:self.object];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
return [self.object methodSignatureForSelector:selector];
}
#end
I have invoked it using
id <MyProtocol> delegate = (TestBO *)[[TestProxyHandler alloc] initWithProtocol:#protocol(MyProtocol) andObject:[TestBO class]];
[delegate testMessage];
But I am not able to make it work in Swift even the initialzier is showing that the message
TestHandler.swift
import Foundation
class TestHandler: NSProxy {
var object: AnyObject
convenience override init(`protocol`: Protocol, andObject clazz: AnyClass) {
if clazz.conformsToProtocol() {
self.object = clazz()
}
else {
NSLog("Error it does not conform to protocol")
}
}
}
Does anyone have any clue to do this in swift ??
EDIT:
In java you can create runtime implementation of a method using the Proxy.newProxyInstance call but can this be achieved in iOS ? using swift ? Any clue ?
Comparing with Objective C and Swift, Swift offers extremely limited access to runtime language access . So based on my research till now it can’t be done :(
I even tried subclassing the NSProxy class in swift but just couldn’t call the super.init and code never compiles but however same thing works in objective C
So I ended up doing this approach
I created a protocol using
#objc protocol SomeProt {
// Some method
}
Note the keyword #objc before protocol is essential else you would not be able to pass it as a variable, also adding #objc limits the usage of protocol to objective c runtime features so don’t expect to get full features of protocols in swift
public func someMethod(`protocol` : Protocol, implementation : AnyClass) {
let isImplemented : Bool = implementation.conformsToProtocol(`protocol`)
// some code
}
If you need to use it in some dictionary or places where it should conform to NSCopying class then use
NSStringFromProtocol
and
NSProtocolFromString
methods
Now I have wrote a objective c helper class to do the initialization
ObjcHelper.h
#import <Foundation/Foundation.h>
#interface ObjcHelper : NSObject
+(NSObject *)objectForClass:(Class)clazz;
#end
ObjcHelper.m
#import "ObjcHelper.h"
#implementation ObjcHelper
+ (NSObject *)objectForClass:(Class)clazz{
return [[clazz alloc] init];
}
#end
Now to use it
let prot : SomeProt = ObjcHelper.objectForClass(NSClassFromString("PROT_HANDLER_CLASS_NAME")) as! SomeProt
However in future if anyone can offer a better answer then please be sure to post it here
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.
I've Objective-C category for iOS 7.1 with property...here's header code
#import <UIKit/UIKit.h>
#interface UIViewController (CategoryName)
#property(nonatomic, copy) UITapGestureRecognizer *recognizer;
then I've .m file with code
#import "UIViewController+CategoryName.h"
#implementation UIViewController (CategoryName)
#dynamic recognizer;
...
- (void)myMethod {
// here it crashes!!!
self.recognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(anothermethod)];
...
}
...
#end
Importing category into viewcontroller and then calling [self myMethod] always crashes with message - unrecognized selector sent to instance pointing to line in myMethod where self.recognizer is initialized.
What am I doing wrong?
There's only 1 way to add properties to an objective-c category: that is by using associated objects.
Please use this guide to fix your code:
http://www.davidhamrick.com/2012/02/12/Adding-Properties-to-an-Objective-C-Category.html
Also, NSHipster did a blogpost on this subject and explains this matter really well:
http://nshipster.com/associated-objects/
To completely help you out of trouble, here's the code you should write to fix your problem:
#import <objc/runtime.h>
NSString * const recognizerPropertyKey = #"recognizerPropertyKey";
#implementation UIViewController (CategoryName)
#dynamic recognizer;
- (void)setRecognizer:(UITapGestureRecognizer *)recognizer {
objc_setAssociatedObject(self, (__bridge const void *)(recognizerPropertyKey), recognizer, OBJC_ASSOCIATION_COPY);
}
- (UITapGestureRecognizer *)recognizer {
return objc_getAssociatedObject(self, (__bridge const void *)(recognizerPropertyKey));
}
#end
After watching iOS tech talks and reading up on class clusters I decided to extract legacy iOS 6 code to a private subclass:
#interface MyUIView : UIView #end // public
#interface MyUIViewiOS6 : MyUIView #end // private
#interface MyUIViewiOS7 : MyUIView #end // private
#implementation MyUIView
+ (id)alloc
{
// Don't loop on [super alloc]
if ([[self class] isSubclassOfClass:[MyUIView class]] &&
([self class] != [MyUIViewiOS6 class]) &&
([self class] != [MyUIViewiOS7 class]))
{
if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_6_1) {
return [MyUIViewiOS6 alloc];
} else {
return [MyUIViewiOS7 alloc];
}
}
return [super alloc];
}
// Common implementation
#end
#implementation MyUIViewiOS6
// Legacy code
#end
#implementation MyUIViewiOS7
// iOS specific code
#end
This implementation works well until I want subclass MyUIView. For example if I create a subclass:
#interface MyRedUIView : MyUIView #end
and then init it like that:
[[MyRedUIView alloc] init]
object of type MyUIViewiOS6 or MyUIViewiOS7 will be allocated instead. Is there a way I can adapt this pattern to support subclassing, so that superclass of MyRedUIView is dynamically switched to MyUIViewiOS6 or MyUIViewiOS7?
You've reached the classic double-inheritance problem. You want to be either a RedUIView or GreenUIView and be either a MyUIViewiOS6 or a MyUIViewiOS7 view.
Since objective-c does not support double-inheritance, you'll have to decide the difference between what you are, and how you act. Anything that determines what you are, you put in the class. Anything that determines how you act goes into a #protocol which then can be implemented.
I would subclass MyUIView since MyUIViewiOS6 and MyUIViewiOS7 correspond to who you are, and then implement a Red or Green protocol for certain functionality:
#interface MyRedUIView : MyUIView<RedProtocol> #end
You can check to see if this class conforms to a specific protocol:
if ([class conformsToProtocol:#protocol(RedProtocol)]) {
self.color = [UIColor redColor];
}
If both of them really are who you are, then you have to use four separate classes.
Here's an example using categories. Assuming that you have MyUIView as specified in the question:
GreenView.h
#import "MyUIView.h"
#import "Green.h"
#interface MyUIView (GreenUIView) <Green>
-(BOOL) isGreen;
#end
#interface GreenView : MyUIView #end
GreenView.m
#import "GreenView.h"
#implementation MyUIView (GreenUIView)
-(BOOL) isGreen{
return [self conformsToProtocol:#protocol(Green)];
}
#end
#implementation GreenView #end
Green.h
#protocol Green <NSObject> #end
AppDelegate.m
#import "GreenView.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
GreenView* view = [[GreenView alloc] init];
NSLog(#"%#", [view isGreen]?#"yes":#"no");
return YES;
}
#end
I am trying to call a method in my data controller object to load the data for my application, but for some reason it is not being called. Below is what I have done to initialize it. Any help would be greatly appreciated.
ViewController:
header file:
#import <UIKit/UIKit.h>
#class DetailViewController;
#class DataController;
#import <CoreData/CoreData.h>
#import "JointCAD.h"
#interface TableViewController : UITableViewController {
}
#property (nonatomic, assign) DataController *dataController;
#end
implementation file:
#import "TableViewController.h"
#import "DataController.h"
#implementation TableViewController
#synthesize dataController;
- (void)viewDidLoad {
[dataController refreshData];
}
#end
Data Controller:
header file:
#import <Foundation/Foundation.h>
#import "JointCAD.h"
#import "JointCADXMLParser.h"
#import "TFHpple.h"
#interface DataController : NSObject {
TFHpple *xpathParser;
}
- (void)refreshData;
- (void)initXMLParser;
- (void)noCallsMessage;
- (void)noInternetMessage;
#end
implementation file:
#import "DataController.h"
#implementation DataController
XMLParser *xmlParser;
- (void)refreshData {
NSLog("Some Method");
}
Is 'dataController' Object being set by some other class? - I believe that's why you have set it as a property? Right?
If No, then Remove the property,#synthesize of 'dataController' and try simple allocation of your 'dataController' object and then try calling your method.
Hope it helps.
You either need to initialize "DataController" prior to actually calling one of it's methods, or you need to make the method, "refreshData" a class by changing it's "-" to a "+".
If you need an instance callback instead. You need to rewrite "viewDidLoad" like this:
- (void)viewDidLoad {
DataController *dataController = [[DataController alloc] init];
[dataController refreshData];
}
And get rid of the property declaration of dataController because you haven't initialized it. If you would prefer a property declaration instead, simply allocate the viewcontroller prior to calling a function from it.
- (void)viewDidLoad {
dataController = [[DataController alloc] init];
[dataController refreshData];
}
One last thing to note is that I (and probably Ray) assume that you're using a storyboard configuration. If you are using a xib configuration, you need to add initWithNibName: to each initialization of the view controller.
I hope that's helpful!