I know default values of IBInspectable-properties can be set as:
#IBInspectable var propertyName:propertyType = defaultValue in Swift. But how do I achieve a similar effect in Objective-C so that I can have default value of some property set to something in Interface Builder?
Since IBInspectable values are set after initWithCoder: and before awakeFromNib:, you can set the defaults in initWithCoder: method.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable NSString *myProp;
#property (assign, nonatomic) BOOL createdFromIB;
#end
#implementation MyView
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if(self != nil) {
self.myProp = #"foo";
self.createdFromIB = YES;
}
return self;
}
- (void)awakeFromNib {
if (self.createdFromIB) {
//add anything required in IB-define way
}
NSLog(#"%#", self.myProp);
}
#end
I wrote my code like this. It works pretty well for me, both when designing in the interface builder or running as a app.
#interface MyView : UIView
#property (copy, nonatomic) IBInspectable propertyType *propertyName;
#end
- (void)makeDefaultValues {
_propertyName = defaultValue;
//Other properties...
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self makeDefaultValues];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[self makeDefaultValues];
}
return self;
}
I'm using like that
#IBInspectable var propertyNameValue:propertyType?
var propertyName:propertyType { return propertyNameValue ?? defaultValue }
if propertyNameValue has nil, propertyName will return defaultValue.
Why don't use use the macro such as:
#if TARGET_INTERFACE_BUILDER
// .....IB only specific code when being rendered in IB
#endif
???
The prepareForInterfaceBuilder selector may also help you to implement IB specific code.
More info about those 2 points here:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_objects_media/chapters/CreatingaLiveViewofaCustomObject.html
Firstly I tried to override getter and did something like this:
- (UIColor *)borderColor {
return _borderColor ?: [UIColor greenColor];
}
But in that case I received issue about undeclared identifier _borderColor.
Ok, I tried to avoid this issue via custom getter.
- (UIColor *)getBorderColor {
return _borderColor ?: [UIColor greenColor];
}
Actually it's not a proper getter, as we don't point this method as getter. In case we point we'll receive issue about undeclared identifier, that's why we won't.
Then we use this method for getting property value in updateUI method.
Also we have to override setter:
- (void)setBorderColor:(UIColor *)borderColor {
_borderColor = borderColor;
[self updateUI];
}
Related
i need to build an application that define an array that should be made of image items.
every image iten has an image, a name and a photographer name.
i build my image item class and i want you to check if my define is correct and good(i just start to learn objective c).
i want you to emphasize on the set's methods.
here is the photoitem.h:
#import <Foundation/Foundation.h>
#interface photoItem : NSObject
{
UIImage *imageView;
NSString *photoNameLabel;
NSString *photographerNameLabel;
UIButton *viewPhoto;
}
#property(readonly) NSString *name;
#property(readonly) NSString *nameOfPhotographer;
#property(readonly) UIImage *imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer: (NSString*)photographerName;
#end
here is my photoitem.m:
#import "photoItem.h"
#implementation photoItem
#synthesize name;
#synthesize nameOfPhotographer;
#synthesize imageItem;
-(id)makePhotoItemWIthPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName
{
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
return self;
}
-(void) setName:(NSString *)name
{
photoNameLabel = name;
}
-(void) setNameOfPhotographer:(NSString *)nameOfPhotographer
{
photographerNameLabel = nameOfPhotographer;
}
-(void)setImageItem:(UIImage *)imageItem
{
imageView = imageItem;
}
#end
i hope you could fix my errors(if there are some).
thanks.
Two problems come to mind:
1) -(id)makePhotoItemWIthPhoto:name:photographer: might be better as -(id)initWithPhoto:name:photographer:. Otherwise the caller needs to alloc and init an object first so that self is valid, then call your method. At that point, the return of self doesn't make sense.
Example:
-(idinitWithPhoto:(UIImage*)image name:(NSString*)photoName photographer:(NSString*)photographerName {
self = [super init];
if (self) {
[self setName:photoName];
[self setNameOfPhotographer:photographerName];
[self setImageItem:image];
}
return self;
}
2) The three readonly properties don't seem to have any purpose since they have no connection to the variables that you initialize in the makePhotoItemWIthPhoto: method.
I'm developing an app for iPhone 3.1.3.
I have the following class:
#interface Pattern : NSObject {
NSMutableArray* shapes;
NSMutableArray* locations;
CGSize bounds;
}
#property (nonatomic, retain, readonly) NSMutableArray* shapes;
#property (nonatomic, retain, readonly) NSMutableArray* locations;
- (id) initWithNumShapes:(int)numShapes screenSize:(CGSize)screenSize;
- (void) addObject:(Object2D*) newObject;
#end
I don't want to let programmers use -(id)init; because I need to setup my fields (shape, locations, bounds) on every initialization.
I don't want to let programmers use this:
Pattern* myPattern = [[Pattern alloc] init];
I know how to implement:
- (id) initWithNumShapes:(int)numShapes screenSize:(CGSize) screenSize{
if (self = [super init]) {
shapes = [NSMutableArray arrayWithCapacity:numShapes];
locations = [NSMutableArray arrayWithCapacity:numShapes];
bounds = screenSize;
}
return (self);
}
How can I do that?
raise an exception if somebody uses the plain init
- (id)init {
[NSException raise:#"MBMethodNotSupportedException" format:#"\"- (id)init\" is not supported. Please use the designated initializer \"- (id)initWithNumShapes:screenSize:\""];
return nil;
}
You can override the init function and give default values from it if you have:
- (id)init {
return [self initWith....];
}
If you don't want init at all, still override and throw some kind of exception saying not to use init.
- (id)init {
NSAssert(NO, #"Please use other method ....");
return nil;
}
This will always give an exception if anyone tried to call init.
I would suggest to use the former case though, and have some default values.
Its always the same schema. Just call init on your superclass (NSObject).
- (id) initWithNumShapes:(int)numShapes screenSize:(CGSize)screenSize {
if(self == [super init]) {
// Custom Init your properties
myNumShapes = numShapes;
myScreenSize = screenSize;
}
return self;
}
I'm new to objective-c and I'm finding that I don't know how to correctly assert that a text property on some given label is equal to a raw string value. I'm not sure if I just need to cast the label as NSString or if I need to modify my assert statement directly.
#interface MoreTest : SenTestCase {
MagiczzTestingViewController* controller;
}
- (void) testObj;
#end
#implementation MoreTest
- (void) setUp
{
controller = [[MagiczzTestingViewController alloc] init];
}
- (void) tearDown
{
[controller release];
}
- (void) testObj
{
controller.doMagic;
STAssertEquals(#"hehe", controller.label.text, #"should be hehe, was %d instead", valtxt);
}
#end
The implementation of my doMagic method is below
#interface MagiczzTestingViewController : UIViewController {
IBOutlet UILabel *label;
}
#property (nonatomic, retain) UILabel *label;
- (void) doMagic;
#end
#implementation MagiczzTestingViewController
#synthesize label;
- (void) doMagic
{
label.text = #"hehe";
}
- (void)dealloc {
[label release];
[super dealloc];
}
#end
The build is fine when I modify the assert to compare a raw NSString to another but when I try to capture the text value (assuming it's of type NSString) it fails. Any help would be much appreciated!
STAssertEquals() checks for identity of the two values provided, so it's equivalent to doing this:
STAssertTrue(#"hehe" == controller.label.text, ...);
Instead, you want STAssertEqualObjects(), which will actually run an isEqual: check like the following:
STAssertTrue([#"hehe" isEqual:controller.label.text], ...);
You need to load the nib of the view controller. Otherwise there won't be any objects for the label outlet to be hooked up to.
One way to do this is to add an ivar for the view controller's view to your test case:
#interface MoreTest : SenTestCase {
MagiczzTestingViewController *controller;
UIView *view;
}
#end
#implementation MoreTest
- (void)setUp
{
[super setUp];
controller = [[MagiczzTestingViewController alloc] init];
view = controller.view; // owned by controller
}
- (void)tearDown
{
view = nil; // owned by controller
[controller release];
[super tearDown];
}
- (void)testViewExists
{
STAssertNotNil(view,
#"The view controller should have an associated view.");
}
- (void)testObj
{
[controller doMagic];
STAssertEqualObjects(#"hehe", controller.label.text,
#"The label should contain the appropriate text after magic.");
}
#end
Note that you also need to invoke super's -setUp and -tearDown methods appropriately from within yours.
Finally, do not use dot syntax for method invocation, it is not a generic replacement for bracket syntax in message expressions. Use dot syntax only for getting and setting object state.
It's the first time I'm trying to use typedef. Admittedly I don't have a very clear idea of what's going on but my understanding was that the values inside typedef get assigned integers starting with 0. I've tried to use them as integers but I get various warnings and errors. One of them is "[NSCFNumber objectForKey:]: unrecognized selector sent to instance". I don't know how to troubleshoot this. I also haven't written dynamic getters/setters much, so my approach might be wrong. Please help.
// MyView.h
typedef enum
{
STYLE_A,
STYLE_B,
STYLE_C,
STYLE_D
} MyShapeStyle;
#interface MyView : UIView
{
MyShapeStyle shapeStyle;
CALayer *myLayer;
MyLayerDelegate *myLayerDelegate;
}
#property (nonatomic) MyShapeStyle shapeStyle;
#property (nonatomic, retain) CALayer *myLayer;
#property (nonatomic, retain) MyLayerDelegate *myLayerDelegate;
#end
// MyView.m
#import "MyView.h"
#implementation MyView
#dynamic shapeStyle;
#synthesize myLayer;
#synthesize myLayerDelegate;
- (id)initWithFrame:(CGRect)frame
{
if ((self = [super initWithFrame:frame]))
{
// Initialization code
MyLayerDelegate *delegate = [[MyLayerDelegate alloc] init];
self.myLayerDelegate = delegate;
CALayer *myLayer = [CALayer layer];
[myLayer setDelegate:delegate];
[self.layer addSublayer:myLayer];
self.myLayer = myLayer;
self.shapeStyle = STYLE_C;
[delegate release];
}
return self;
}
-(MyShapeStyle)shapeStyle
{
return [[self.myLayer valueForKey:#"style"] integerValue];
}
- (void)setShapeStyle:(MyShapeStyle)style
{
[self.myLayer setValue:[NSNumber numberWithInt:style] forKey:#"style"];
}
// MyLayerDelegate.m
-(void)drawLayer:(CALayer *)theLayer inContext:(CGContextRef)theContext
{
int id = [[theLayer valueForKey:#"style"] integerValue];
if( id == STYLE_A )
{
}else if ( id == STYLE_B ){
}
}
There is no reason to use valueForKey: in that code; just get/set the various properties directly.
-(MyShapeStyle)shapeStyle
{
return (MyShapeStyle) self.myLayer.style;
}
There is also no need for the #dynamic in that code. That is only needed if you are going to dynamically generate the methods.
As for why the objectForKey: does-not-respond error, there isn't anything in that code that should trigger that. Could be a retain/release issue or it could be a problem in some other code that you haven't shown.
I initialize a view(Image) through:
Image *myImageView = [[Image alloc]init];
myImageView.myId = randomImageNumber;
[myImageView initWithImage:myImage];
At the Image class I do a Log(LOG1) and get the previously set randomImageNumber.
Later on, in the very same Class, I do a second Log(LOG2).
Why does my second log have no value anymore ?
Here my implementation-file of the Class Image:
#synthesize myId;
-(id) initWithImage: (UIImage *) anImage
{
NSLog(#"LOG1%d",myId);
if ((self = [super initWithImage:anImage]))
{
self.userInteractionEnabled = YES;
}
return self;
}
}
-(void)touchesBegan...
....
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
NSLog(#"LOG2%d",myId);
}
The "return self" empties myId which i declared in the header-file and which was set at the initialisation.
How do I prevent that ?
my Headerfile looks like this:
#interface Image : UIImageView
{
int myId;
}
#property (assign) int myId;
#end
I think I found it:
https://www.google.com/maps/place/Variable/#53.626739,10.025728,17z/data=!3m1!4b1!4m2!3m1!1s0x47b1885360fab615:0x584b82c7dfb5f612
Can you check if this Variable is yours?
besties
phil
Couple things in your code. NEVER call init more than once on an object, that just screws up your object.
Change it to this:
Image *myImageView = [[Image alloc] initWithImage:myImage];
myImageView.myId = randomImageNumber;
That is your problem, by default when initializing a subclass of NSObject, all properties are set to 0 (or nil if they are pointers).
If you need to have a default value for myId then do this:
// Image.m
#implementation
// other code
-(id) initWithImage:(UIImage *) image
{
if (self = [super initWithImage:image])
{
self.myId = randomImageNumber;
}
return self;
}
// other code
#end