Swift alternative to this Obj-C pattern using #dynamic? - swift

In Obj-C, I sometimes used the dynamic keyword in controllers to make working with their custom views more convenient. In this example, I wouldn't need to typecast self.view every time I wanted to access a property or call a function:
// MyServiceController.m
#dynamic view;
-(void)loadView
{
self.view = MyServiceView()
self.view.delegate = self;
}
-(void)someFunc
{
[self.view doSomething];
}
// MyServiceView.h
#property(nonatomic, strong)id<MyServiceViewDelegate> delegate;
-(void)doSomething;
Is there a way to achieve this with pure swift?

Related

How to access values from a different UIViewController

How can I access the value from an inputField located in a second viewController?
The class name of the second view controller is SettingsViewController and the outlet name for the inputField is setRateInput.
I tried this but it didn't work…
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
when I NSLog it comes out as The value is: (null)
Any idea what I'm doing wrong?
Here is the implementation file for the main viewController:
#import "SettingsViewController.h"
#interface ViewController ()
#property (strong, nonatomic) SettingsViewController * settings;
#end
#implementation ViewController
// lazy instantiation
-( SettingsViewController *) settings
{
if (_settings == nil) {
_settings = [[SettingsViewController alloc]init];
}
return _settings;
}
- (IBAction)calculatePrice:(id)sender {
double taxRateFromInput = [[self.settings.setRateInput text]doubleValue];
#end
In theory, you could create a global. Create a new class, call it something like taxRate (.h and .m)
In taxRate.h, add the following code:
#import <Foundation/Foundation.h>
#class MyTaxRate;
#interface TaxRate : NSObject {
}
#property (nonatomic, retain) double * taxRateFromInput;
+(TaxRate*)getInstance;
#end
Then, in your controller, put a "#import taxRate.h" in there. In your .m file, add the following:
#import "TaxRate.h"
#implementation TaxRate
#synthesize taxRateFromInput;
static TaxRate *instance =nil;
+(TaxRate *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance= [TaxRate new];
}
}
return instance;
}
#end
Note: This is extremely similar in structure to what I'm purposing.
if you have the reference from the object view controller you can just access by the property from your attribute.
You instantiated a new SettingsViewController, but you didn't do anything to instantiate its textfield setRateInput. You can do it when you instantiate it:
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
or, as a beter solution, instantiate the text field in -init of SettingsViewController
- init {
if (self = [super init] {
self.setRateInput = [UITextField alloc] initWithFrame:someFrame]];
}
return self;
}
If you use nib files, this would be a lot easier.
Note: setRateInput is a bad name for a property. Consider rateTextField instead.
Edit I forgot to add that you have to add the text field as a subview to its parent view.
So it will be like,
_settings = [[SettingsViewController alloc]init];
_settings.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[_settings.view addSubView:_settings.setRateInput];
In this case, the setRateInput is retained by its super view. You're not using ARC, so you can call autorelease on your text field.
The better solution: Use - (void) loadView; inside SettingsViewController. Loading the view is the responsibility of the correspondent view controller.
- (void) loadView {
self.setRateInput = [[UITextField alloc] initWithFrame:someFrame] autorelease];
[self.view addSubView:_settings.setRateInput];
}
Edit: xib files and storyboards can help you out. Give these tutorials a try.
You are on the right track, also well done with your lazy instantiation (as
a demonstration that you grasped the concept, I mean).
But note, that outlets don't get connected until viewDidLoad is called. So if you
just alloc/init your viewController (lazily), the outlet to your textfield is pointing to nil.
The outlet doesnt get connected until your controller's view property is accessed, ie the view is displayed.
What you could do is give the settings viewController a handle to your calculating viewController and let it set a public property on the calculating viewController that represents the rate.
This is a common pattern - delegation - where one viewController (settingsViewcontroller) calls a method on its delegate (calculating viewController).
You wouldn't need the settingsViewcontroller property in your calculating viewController then, but just instantiate a new settings viewController every time you want it to be brought up, giving it a reference to your calculating viewController.
Another possibility - maybe even better - is to define a model object that does calculation and takes care of the rate it needs to calculate. Then you could give your settingsViewcontroller a reference to that model object (probably instantiated in your
other viewController), so that it can change the rate on it.
PS: also re think how you instantiate viewControllers generally. The designated initialiser is -initWithNibName:bundle: - so usually, you wouldn't just alloc/ -init them.
If you use storyboards (you probably should!), use storyboard's -instantiateViewControllerWithIdentifier: or use the above mentioned designated initialiser.

How to create a proxy protocol like UIAppearance

I know how to create a protocol already but I'm wondering what would be the best practice to create a proxy protocol like Apple did for the UIAppearance protocol and the implementation on certain UI classes.
Why I want to do it this way? Because I already have a lot of UI classes and I would like to centralize the implementation of the code for changing color.
Maybe an odd question but my curiosity drove me to this point.
Thanks
Just make the proxy a static object and access it through class-level methods, the same way you'd implement a singleton, e.g.
#implementation MyClass
+ (MyProxyObject *)proxy
{
static MyProxyObject *sharedProxy = nil;
if (sharedProxy == nil)
{
sharedProxy = [[MyProxyObject alloc] init];
}
return sharedProxy;
}
#end
Then for any property of your class, e.g. textColor, just have your class use the value in [[self class] proxy].textColor instead of storing its own value. E.g.
#interface MyClass : UIView
#property (nonatomic, strong) textColor
#end
#implementation MyClass
- (UIColor *)textColor
{
return textColor ?: [[self class] proxy].textColor
}
#end
If you need a way to refresh your onscreen views immediately whenever a property on the proxy is changed, you could do that by having the proxy broadcast an NSNotification in its textColor setter method, and have all the instances observe that notification and call setNeedsDisplay on themselves when they receive it.

In Objective-C with no nib file is MVC to have the logic together with the view in same class?

When I work in Objective-C programatically with out nib files, and have the logic in my:
appViewController.m
having in the same class What is going on with that view, as well as with the View elements? Is this against the MVC pattern?
Do I have to create another class and message both classes?
It's up to you! If you want to separate layers (M,V,C) you can create your own view programmatically and, by using composite design pattern, build it in your UIView subclass, by removing drawing code from your controller.
That is...
You create a "CustomCompositeView" that extends UIView
in layoutSubview (hinerited from UIView) you will draw all your UI elements
in your CustomViewController you will display your view using loadView:
code:
- (void)loadView
{
CustomCompositeView *mainView = [[CustomCompositeView alloc] initWithFrame:aFrame];
[self setView:mainView];
[mainView release]; // remove this line if you are using ARC!
}
Technically, it is going against the MVC pattern. Your V and C and combined into a single object. You can seperate the code that handles layout and drawing into a seperate UIView subclass. Then load it with loadView:
// MyViewController.m
- (void)loadView {
MyView* myView = [[[MyView alloc] init] autorelease];
myView.delegate = self;
self.view = myView;
}
#pragma mark - MyViewDelegate Methods
- (void)myViewSaveButtonWasPressed:(MyView *)myView {
// do something
}
To communicate between the view and the view controller, you can define a delegate protocol.
// MyView.h
#class MyView;
#protocol MyViewDelegate <NSObject>
- (void)myViewSaveButtonWasPressed:(MyView *)myView;
#end
#class MyView : NSObject
#property (nonatomic, assign) id<MyViewDelegate> delegate;
// ...
When a button is pressed in the view (or something else along those lines) pass that on to the delegate. The ViewController should conform to the delegate method and handle the actual logic itself that way.

Returning an instance of a class within its self

i am trying to write a UIToolbar subclass and am having issues. This is the first time i have truly tried to sub class anything so forgive me if it is something stupid. I cant work out how to return an instance of the class from within its self.
eg
keyboardToolBar *toolbar;
#property (nonatomic, retain) keyboardToolBar *toolbar;
toolbar = [[keyboardToolBar alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 50)
-(keyboardToolBar*)initWithFrame:(CGRect)frame{
keyboardToolBar *keyboard = [[keyboardToolBar alloc] initWithFrame:frame];<--how can i do this
//do stuff with keyboard
return keyboard;
}
I assume the above will be stuck in an endless loop of calling init which calls init and so on, so how do you return an instance of a class from within its own init method or is there a better way of doing this.
thanks
darc
There are many things that are not clear here. Are you trying to create convenience method? in which case you should call it keyboardToolbarWithFrame: and implement it like
+ (id)keyboardToolbarWithFrame:(CGRect)frame {
return [[[self alloc] initWithFrame:frame] autorelease];
}
Note the + sign indicating that it is a class method. This will give you an autoreleased instance of your class.
Now if you were just trying to class its super class for necessary initialization, you can just remove the method. It will call the super class' initWithFrame: method. But usually your own implementation will be something like this,
- (id)initWithFrame:(CGRect)frame {
self = [super init];
if ( self ) {
// Do your class initializations here.
}
return self;
}
Did you check the sample projects here: http://developer.apple.com/library/ios/#documentation/uikit/reference/UIToolbar_Class/Reference/Reference.html

UIView and UIViewController practice for inheritance

I was just wondering if this approach looks like a good practice for apps with a lot of custom views, for nested PNG graphics and animations that may change based on user interaction. I created a BaseView class that extends UIView
#interface BaseView : UIView {
#protected
BaseViewController *controller;
}
#property (retain) BaseViewController *controller;
#end
and a corresponding controller class which is the primary location which I am putting code to manipulate the view
#interface BaseViewController : UIViewController {
#protected
CGRect drawArea;
}
- (void) drawArea:(CGRect) _drawArea;
- (CGRect) drawArea;
- (void) linkSubviewController: (BaseViewController *) _subviewController;
#end
where "drawArea" is the CGRect used to pass to the view as a frame.
"linkSubviewController" allows you to nest a controller and view as follows :
- (void) linkSubviewController: (BaseViewController *) _subviewController {
[self.view addSubview:[_subviewController view]];
}
In addition I layered another custom pair called "ImageView" and "ImageViewController" which extend BaseView but also store a UIImage and an x,y,w,h
In the "drawRect" drawing methods on views I can check to see if any vars in the self.controller vars have been changed, or assign images, for example :
UIImage *image = [(ImageViewController *)self.controller image];
CGContextDrawImage( ... ) etc
I write most of the loadView methods something like this
- (void)loadView {
ImageView *v = [[ImageView new] initWithFrame:drawArea];
v.controller = self;
self.view = v;
}
The base "initWithFrame" routine contains
self.backgroundColor = [UIColor clearColor];
self.opaque = NO;
So I can load a variety of images with transparent backgrounds without having to assign that each time.
I've been able to use this code throughout my app and it seems to make it easy to write a top level class which assembles the layout of custom items. For animations I've been putting them in the controllers and manipulating self.view.layer.
Basically I am looking for feedback, I am new with Objective-C and the IPhone SDK
There are several issues here:
Using [[Classname new] init...] is incorrect usage of new. Using new is short for [[Classname alloc] init] so you are effectively calling init twice.
Views shouldn't really need to know who is controlling them.
Your view is retaining the controller, and since UIViewController retains its view, you have a retain cycle and neither will ever be fully released.
If you want this type of behavior (whereby a view can delegate its drawing to a parent), try creating a DrawDelegate protocol, have your controllers implement that protocol, and in your view subclass have a non-retaining drawDelegate property:
#protocol DrawDelegate
- (void) drawArea:(CGRect)rect;
#end
#interface BaseView : UIView {
id<DrawDelegate> drawDelegate;
}
#property (assign) id<DrawDelegate> drawDelegate;
#end