I am converting some Swift code to Objective C. not much familiar with Swift syntax.
override var layer: CAShapeLayer {
get {
return super.layer as! CAShapeLayer
}
}
I don't understand how to override this property in objective C.
I tried this
in .h file
#property (nonatomic, readonly, retain) CAShapeLayer *layer;
in .m file
#synthesize layer;
but it seems to not work. There is no error but the code is not executing as expected.
What you need to do now is just implement the getter for the layer property:
-(CAShapeLayer *)layer
{
return (CAShapeLayer *)super.layer
}
Related
I've spent quite a bit of time figuring this out..and haven't found any success.
Put simply, I need a swift subclass of an objective c class to be able to override a certain property declared in the .m file inside of an extension. How would I do this? Essentially the property needs to be replaced with a custom subclass.
#interface SomeClass : NSObject
{
}
#end
//SomeClass.m
#interface SomeClass ()
#property (nonatomic, strong) UIScrollView scrollView;
#end
//Swift class
class NewClass: SomeClass {
let scrollViewSubclass = MyScrollView()
override var scrollView: UIScrollView! {
return scrollViewSubclass
}
}
Something like that.. just not sure how to do it. I can't even override scrollView because the swift subclass doesn't know it exists!
Swift (from the book 《iOS Animations by Tutorials:Chapter 12》 released by http://www.raywenderlich.com/):
let photoLayer = CALayer()
#IBInspectable
var image: UIImage! {
didSet {
photoLayer.contents = image.CGImage
}
}
How can I implement the above syntax in objective-c?
I know only to set the property of photoLayer and image like below:
#property (strong, nonatomic) CALayer *photoLayer;
#property (strong, nonatomic) IBInspectable UIImage *image;
But i do not know how to implement didset{...} parts using objective-c syntax, please help!
override the setter and implement the setter yourself.
- (void)setImage:(UIImage *)image {
if (_image != image) {
_image = image;
photoLayer.contents = image.CGImage;
}
}
Swift needs special language features for this, because of its static nature, one cannot subclass at runtime.
In Objective-C this is not necessary, so you have no language feature for this. You can use key-value observation.
But if the observing instance is the instance whose property changed, you can do it with subclassing as mentioned by #Daij-Djan. (Basically this is what you do in Swift.) Additionally in Objective-C you have the ability to observe the properties of a different instance of a different class.
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.
Edit:
I'm drawing a line using drawRect: function which resides in a custom class in a view which I created in the MainStoryboard.storyboard file. The view does point to the custom class (draw2D) in the interface builder.
All code i have regarding draw in my main file(viewController) is now:
(header file)
#class draw2D;
#property (weak, nonatomic) IBOutlet draw2D *draw;
(m file)
#import "draw2D.h"
[draw.listOfFreqToDraw addObject: closestChar];
[draw setNeedsDisplay];
The problem I seem to have now is that it only runs the drawRect method in draw2D class once and never calls it again (therefor listOfFreqToDraw isn't even called after the first run)
You can always change a variable in draw2D
Set this up in the draw2D.h
#property (nonatomic, strong) NSMutableArray * listOfFreqToDraw;
in draw2D.m
- (void)drawRect:(CGRect)rect {
for (UIBezierPath *path in self. listOfFreqToDraw) {
[path stroke];
}
}
and in your main class
[draw.listOfFreqToDraw addObject: closestChar];
[draw setNeedsDisplay];
I'm building a MapKit based app for iPhone.
I have a number of MKPolylines added to the map.
However, instead of having a MKPolyline, I would like to have my own Model class conforming to the MKOverlay protocol added to the map so that I can access the model properties when creating the corresponding view in mapView:viewForOverlay.
The problem is that I can't find the way to inherit from MKPolyline because it doesn't have any init methods that I can call from the subclass' init. You can only create them using the convenience methods.
How can I bring together the model properties and the MKPolyline behaviour?
MANIAK_dobrii's code is the way to go but I found I had to implement some additional MKMultiPoint methods to get it to work, here are my complete header and implementation files for an AnchorLine class I used:-
Header AnchorLine.h
#import <MapKit/MapKit.h>
#interface AnchorLine : NSObject <MKOverlay> {
MKPolyline* polyline;
}
#property (nonatomic, retain) MKPolyline* polyline;
+ (AnchorLine*)initWithPolyline: (MKPolyline*) line;
#end
Implementation AnchorLine.m
#import "AnchorLine.h"
#implementation AnchorLine
#synthesize polyline;
+ (AnchorLine*)initWithPolyline: (MKPolyline*) line {
AnchorLine* anchorLine = [[AnchorLine alloc] init];
anchorLine.polyline = line;
return [anchorLine autorelease];
}
- (void) dealloc {
[polyline release];
polyline = nil;
[super dealloc];
}
#pragma mark MKOverlay
//#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (CLLocationCoordinate2D) coordinate {
return [polyline coordinate];
}
//#property (nonatomic, readonly) MKMapRect boundingMapRect;
- (MKMapRect) boundingMapRect {
return [polyline boundingMapRect];
}
- (BOOL)intersectsMapRect:(MKMapRect)mapRect {
return [polyline intersectsMapRect:mapRect];
}
- (MKMapPoint *) points {
return [polyline points];
}
-(NSUInteger) pointCount {
return [polyline pointCount];
}
- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range {
return [polyline getCoordinates:coords range:range];
}
#end
Hope that helps someone.
You can set an associated object attribute of the class. This allows you to bind an instance variable to an existing class. Make sure you properly clean up after yourself.
It is true that MKPolyline doesn't have its own init method. In fact the only class in MKPolyline's inheritance chain that does have an init method is NSObject.
So when I subclassed MKPolyline I just overrode the init method defined by NSObject...
-(id) init {
self = [super init];
if(self) {
//my initialization here
}
return self;
}
Then when you want to instantiate your subclass with coordinates you might do something like this...
-MyPolyline* myPolyline = (MyPolyline*)[MyPolyline polylineWithCoordinates:coordinates count:coordinateCount];
UPDATE: There's another option (could be better) to use message forwarding for this (like -forwardingTargetForSelector or stuff).
I had the same issue today but came up with other solution. Instead of using suggested by Wayne associated object attribute stuff I just encapsulated MKPolyline in another class and transferred MKOverlay protocol's messages to it.
So I've got something like in .h:
#interface MyOverlay : NSObject <MKOverlay>
{
MKPolyline* polyline;
id object;
}
#property (nonatomic, retain) id object;
#property (nonatomic, retain) MKPolyline* polyline;
+ (MyOverlay*)myOverlayWithObject: (id)anObject;
#end
And in .m:
#implementation MyOverlay
#synthesize object;
#synthesize polyline;
+ (MyOverlay*)routePartOverlayWithObject: (id)anObject {
MyOverlay* myOverlay = [[MyOverlay alloc] init];
... generating MKPolyline ...
myOverlay.polyline = ... generated polyline ...;
routePartOverlay.object = anObject;
return [myOverlay autorelease];
}
- (void) dealloc {
[cdRoutePart release]; cdRoutePart = nil;
[polyline release]; polyline = nil;
[super dealloc];
}
#pragma mark MKOverlay
//#property (nonatomic, readonly) CLLocationCoordinate2D coordinate;
- (CLLocationCoordinate2D) coordinate {
return [polyline coordinate];
}
//#property (nonatomic, readonly) MKMapRect boundingMapRect;
- (MKMapRect) boundingMapRect {
return [polyline boundingMapRect];
}
- (BOOL)intersectsMapRect:(MKMapRect)mapRect {
return [polyline intersectsMapRect:mapRect];
}
#end
So MyOverlay behaves like MKPolyline (conforms to MKOverlay) and at the same time I can do anything with it, having as many properties as I need.
I rewrote jmathew's excellent answer which I didn't find anywhere else, and it works like charm. A little modified version in Swift:
final class CustomPolyline: MKPolyline {
private(set) var color: UIColor?
private(set) var width: CGFloat?
convenience init(coordinates: [CLLocationCoordinate2D], color: UIColor, width: CGFloat) {
self.init(coordinates: coordinates, count: coordinates.count)
self.color = color
self.width = width
}
}
Whats mentioned here so far hasn't quite worked for me but I managed a solution based on the other answers and some independent research. I am not 100% certain in this but you can cast a MKPolyline into a custom sub-class only if you use the static method call that calls the right 'init' method internally.
(CustomPolyline*)[CustomPolyline polylineWithCoordinates:coordinates count:coordinateCount]
The above won't work because polylineWithCoordinates only allocates memory for an MKPolyline object and not CustomPolyline. I suspect what's happening internally is that polylineWithCoordinates calls another initializer method in a manner similar to: [MKPolyline otherInitMethod:...]. And its not allocating the proper amount of memory because its now using an MKPolyline static method call and not our CustomPolyline static call.
However if we use
(CustomPolyline*)[CustomPolyline polylineWithPoints:polyline.points count:polyline.pointCount];
It does work. I think this is because polylineWithPoints is using an initializer that returns an id not just chaining to another method call. And since we called it using the CustomPolyline class the initializer allocates memory for CustomPolyline not MKPolyline.
I could be completely wrong on why it works. But I've tested this and it seems to work fine. MKPolygon can be extended in a similar manner. In that case I think the correct static method to use is MKPolygon polygonWithCoordinates:points count:pointSet.count]]
My implementation for reference:
CustomPolyline.h
#import <MapKit/MapKit.h>
typedef enum {
CustomPolylineTypeNone = 0,
CustomPolylineDifferentStrokes
} CustomPolylineType;
/**
* CustomPolyline wraps MKPolyline with additional information about a polyline useful for differentiation.
*/
#interface CustomPolyline : MKPolyline
#property CustomPolylineType type;
-(CustomPolyline*)initWithMKPolyline:(MKPolyline*)polyline;
#end
CustomPolyline.m
#import "CustomPolyline.h"
#implementation CustomPolyline
#synthesize type;
/**
* Takes an MKPolyline and uses its attributes to create a new CustomPolyline
*/
-(CustomPolyline*)initWithMKPolyline:(MKPolyline*)polyline
{
// We must use the this specific class function in this manner to generate an actual
// CustomPolyline object as opposed to a MKPolyline by a different name
return (CustomPolyline*)[CustomPolyline polylineWithPoints:polyline.points count:polyline.pointCount];
}
#end