How do I set a nonzero initial value in iOS? - ios

I have an ivar which is mentioned in my header
#interface MyClass : UIView{
int thistone;}
- (IBAction)toneButton:(UIButton *)sender;
#property int thistone;
#end
and I have synthesized it in the implementation:
#implementation MyClass
#synthesize thistone;
- (IBAction)toneButton:(UIButton *)sender {
if(thistone<4)
{thistone=1000;} // I hate this line.
else{thistone=thistone+1; }
}
I cannot find (or find in any manual) a way to set a nonzero initial value. I want it to start at 1000 and increase by 1 each time I press the button. The code does exactly what I intend, but I'm guessing there's a more proper way to do it that saves me the if/else statement above. Code fixes or pointers to specific lines in online documentation greatly appreciated.

Every object has a variant of the init method called at instantiation. Implement this method to do such setup. UIView in particular have initWithFrame: and initWithCoder. Best to override all and call a separate method to perform required setup.
For example:
- (void)commonSetup
{
thisTone = 1000;
}
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame])
{
[self commonSetup];
}
return self;
}
- (id)initWithCoder:(NSCoder *)coder
{
if (self = [super initWithCoder:coder])
{
[self commonSetup];
}
return self;
}

Related

Custom Constructor in Objective-C

I have created a custom class extending UIView. This class has some methods such as Drawerect...
Up to now, I was just putting it in my storyboard and telling that it belongs to the class. I would now allocate and place those objects dynamically. Is there a method so I could call :
[[MyObj alloc] initWithFrame:....]
Id be glad to find any help !
You can create your own constructor in the header file of your class.
The return value is of type id , in its declaration in the main file you need to call a super initialization (for example self = [super initWithFrame:CGRect]) and then return the self. You can customize the parameters of your constructor in the header file to fit your needs.
Example for UIView:
.h:
#import <UIKit/UIKit.h>
#interface CustomView : UIView
-(id)initWithFrame:(CGRect)frame backgroundColor:(UIColor *)backgroundColor;
.m:
#import "CustomView.h"
#implementation CustomView
-(id)initWithFrame:(CGRect)frame backgroundColor:(UIColor *)backgroundColor{
self = [super initWithFrame:frame];
if (self) {
//after allocation you could set variables:
self.backgroundColor = backgroundColor;
}
return self;
}
#end
When instances of UIView are unarchived from an Interface Builder document, their initWithFrame: method isn't called. Instead, the unarchiver calls initWithCoder:. Ideally you should override both methods, and have them call a common method that provides a shared implementation of the initialization code. That way the views will be initialized correctly whether they're instantiated programmatically, or as a result of being unarchived. Here's an example:
- (id)initWithFrame:(CGRect)frame
{
if (!(self = [super initWithFrame:frame])) return nil;
[self configureSubviews];
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (!(self = [super initWithCoder:aDecoder])) return nil;
[self configureSubviews];
return self;
}
- (void)configureSubviews
{
// Custom configuration code...
}

Confused about addTarget pointer behavior for addTarget:action:forControlEvents:

I have a UIView subclass with a delegate property. In the init method, I set
self.delegate = nil.
The view also has a button, so in the init method, I also set the target of the button to be self.delegate, which is nil:
[myButton addTarget:self.delegate action:#selector(buttonAction) forControlEvents:UIControlEventTouchUpInside]
In the UIViewController that sets up my UIView subclass, I call a method in the UIView that sets the UIView's self.delegate to the UIViewController. When I click the button, the change in target seems to be reflected.
I am wondering how this ends up working, as my understanding is that addTarget:action:forControlEvents takes an id as the target, and pointers should be pass by value in Obj-C. Thus, I am pretty confused about why the originally nil-valued pointer was updated after the addTarget method was already called.
The right way to do that is declaring a protocol for your view, which will delegate for button's tap action, i.e.
YourView.h
#class YourView;
#protocol YourViewDelegate
#optional
- (void)customView:(YourView *)view didSelectButton:(id)button;
#end
#interface YourView : UIView
//...
#property (weak, nonatomic) id <YourViewDelegate> delegate;
#end
YourView.m
#interface YourView()
#end
#implementation
- (id)init
{
if (self = [super init]) {
//...
[self setup];
}
return self;
}
- (void)awakeFromNib
{
//...
// setup logic when this view created from storyboard
[self setup];
}
- (void)setup
{
[myButton addTarget:self
action:#selector(buttonTapped:)
forControlEvents:UIControlEventTouchUpInside];
}
- (void)buttonTapped:(id)sender
{
if (self.delegate && [self.delegate respondsToSelector:#selector(customVIew:didSelectButton)] {
[self.delegate customView:self didSelectButton:sender];
}
}
#end
Then, in your view controller implement YourViewDelegate category:
#interface YourViewController()
//...
#end
#implementation
- (void)viewDidLoad
{
[super viewDidLoad];
//...
self.yourView.delegate = self;
}
//...
- (void)customView:(YourView *)view didSelectButton:(id)button
{
//do your stuff
}
#end
Objective-C uses Dynamic binding. Method to invoke is determined at runtime instead of at compile time. Which is why it is also referred to as late binding.
Reference link -
https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/DynamicBinding.html
So what will be the delegate and which method is being called is defined at runtime.

Custom UITextField Freezes App When Opening Emoji Keyboard?

This is the weirdest thing I've ever encountered, and the problem is consistent across an iPhone 4 and iPhone 5s...
I've created a subclass of UITextField to add some custom functionality, and have set a UITextField's custom class to my custom class in the storyboard.
Great, all good - but.. Anytime I tap to open the field, I can type fine - as soon as I hit the emoji globe part of the keyboard, the app instantly locks up. It doesn't crash, I just cannot do anything, the typing indicator stops blinking and the app just hangs infinitely..
So very strange.
Here's the custom class I've created.
Header
#import <UIKit/UIKit.h>
#interface GenericTextField : UITextField <UITextFieldDelegate>
#property id nextField;
#property id nextCallbackTarget;
#property SEL nextCallbackSelector;
#end
Implementation
#import "GenericTextField.h"
#implementation GenericTextField
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self setup];
}
return self;
}
- (void)setup
{
self.delegate = self;
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldReturn:(UITextField *)textField
{
if ([self.nextField respondsToSelector:#selector(becomeFirstResponder)]) {
[self.nextField becomeFirstResponder];
} else {
[self resignFirstResponder];
if ([self.nextCallbackTarget respondsToSelector:self.nextCallbackSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[self.nextCallbackTarget performSelector:self.nextCallbackSelector];
#pragma clang diagnostic pop
}
}
return NO;
}
#end
Here's another interesting part. If I comment out the portion where I'm setting the delegate to self, everything works fine.. However, if I comment out my custom delegate methods and keep the delegate set to self in the setup method, I still get the freeze. This seems to be specifically linked to setting the delegate? Why? Does it have to do with setting it to self from the initWithCoder? If so, why does it just happen in UITextField and none of my other custom classes that set delegates to self like in a UIScrollView subclass I've made?
Alright, took a little searching but maybe this answer will help anyone trying to figure it out.
UITextField is unique AFAIK. You can usually use a class as its own
delegate with no particular problems. For UITextField you must create
an actual delegate (that could, of course, call methods on the
UITextField for which it's a delegate. Just be careful to avoid retain
loops, even if you're using ARC).
Which led me to trying some updated code in my implantation and creating a shared delegate class for handling the delegate in the simplest way I could think of. Here's the updated implementation.
Hope it helps!
#import "GenericTextField.h"
#interface GenericTextFieldDelegate : NSObject <UITextFieldDelegate>
+ (GenericTextFieldDelegate *)sharedDelegate;
#end
#implementation GenericTextFieldDelegate
+ (GenericTextFieldDelegate *)sharedDelegate
{
static GenericTextFieldDelegate *genericTextFieldDelegate;
#synchronized(self) {
if (!genericTextFieldDelegate) {
genericTextFieldDelegate = [[GenericTextFieldDelegate alloc] init];
}
}
return genericTextFieldDelegate;
}
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
return YES;
}
- (BOOL)textFieldShouldReturn:(GenericTextField *)textField
{
if ([textField.nextField respondsToSelector:#selector(becomeFirstResponder)]) {
[textField.nextField becomeFirstResponder];
} else {
[textField resignFirstResponder];
if ([textField.nextCallbackTarget respondsToSelector:textField.nextCallbackSelector]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[textField.nextCallbackTarget performSelector:textField.nextCallbackSelector];
#pragma clang diagnostic pop
}
}
return NO;
}
#end
#implementation GenericTextField
- (id)initWithCoder:(NSCoder *)aDecoder
{
if (self = [super initWithCoder:aDecoder]) {
[self setup];
}
return self;
}
- (void)setup
{
self.delegate = [GenericTextFieldDelegate sharedDelegate];
}
#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