'Casting' a UIView defined in Storyboard to a subclass - ios

This might be a really bad question, so I apologize in advance. Feel free to tell me if there is a better way to approach this.
I'm using storyboards to layout the initial arrangement of objects. Say that I put a UIView on the storyboard, and I link that view to a property in my ViewController.m file called storyboardView. At runtime, a lot might happen to that UIView, and following the MVC pattern, I'd like the code that governs that behavior to exist in a separate subclass. How can I 'cast' that UIView so that it is now responds to the subclass rather than the ViewController?
I'm thinking of something along these lines, but this doesn't work. It doesn't throw any errors, but the background color isn't turning red, so I know that I am unsuccessful:
#import "ViewController.h"
#interface ViewController ()
#property (weak, nonatomic) IBOutlet UIView *storyboardView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.storyboardView = [[MyView alloc] initWithFrame:self.storyboardView.frame];
}
#end
Subclass Header:
#import <UIKit/UIKit.h>
#interface MyView : UIView
#end
Subclass Implementation:
import "MyView.h"
#implementation MyView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor redColor];
}
return self;
}
#end

In storyboard you can change the UIView to any subclass like this

Related

Why does subclassing the main ViewController's view causes delegates to never fire?

I made a customView that implements its own delegate and set it up inside the main viewController. It works perfectly well, it's the standard practice, there's no need to show any code.
For the sake of better organizing the code I created a manager called customManager that contains the customView and the main viewController's view.
Instead of having all the code that it's needed inside the viewController (managing how the customView is setup and also listening to its delegate) I moved all that code to the customManager instead. That's why I made the customManager just to clear up the code that's inside the viewController.
All renders properly but the delegate never fires.
CustomManager.h
#import "CustomView.h"
#interface CustomManager : NSObject <CustomViewDelegate>
#property (weak) UIView *view; // viewController's view
#property (strong) CustomView *customView;
- (void)load;
#end
CustomManager.m
#import "CustomManager.h"
#implementation CustomManager
// Initialization Method
- (void)load {
_customView = [[CustomView alloc] init];
_customView.delegate = self; // !!!
_customView.frame = CGRectMake(0.0, 20.0, _view.frame.size.width, _view.frame.size.height - 20.0);
[_view addSubview:_customView];
}
#pragma mark - CustomViewDelegate
// Delegate Methods
#end
ViewController.h
#import "CustomManager.h"
#interface ViewController : UIViewController
#property (strong) CustomManager *customManager;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_customManager = [[CustomManager alloc] init];
_customManager.view = self.view;
[_customManager load];
// 1. The load method above removes the code needed here for initializing the customView and setting it up correctly.
// 2. The delegate methods are now in the customManager and leave the space below clear.
}
#end

How do you use UIView with properties or variables inherited from ViewController?

I have UIViewController named ParentViewController.h and .m
Then I added UIView inside this ParentViewController.
I had uiview.h and uiview.h added and assigned to UIView inside ParentViewController.
From
-(void)drawRect:(CGRect)rect {}
which is located in uiview.m, I need to access to properties inside ParentViewController.
How do I do this? Am I using UIView wrong?
ParentViewController.h
#import <UIKit/UIKit.h>
#interface ParentViewController : UIViewController
//I want my uiview to access this variable.
#property (nonatomic, strong) NSMutableArray *usedByUIView;
#end
ParentViewController.m
#import "ParentViewController.h"
#import "uiview.h"
#implementation ParentViewController
- (void)viewDidLoad {
...
}
#end
uiview.h
#import <UIKit/UIKit.h>
#interface uiview : UIView
#end
uiview.m
#import "uiview.h"
#implementation uiview
-(id) initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if(self){
}
return self;
}
- (void)drawRect:(CGRect)rect {
NSLog(#"start drawing using the data from usedByUIView");
}
#end
There are a few answers on this subject but, summarizing them, you don't, at least not the way that you're doing it. UIView's do not have access to their view controller's and aren't supposed to need access. Of course, in the real world, sometimes it's not worth the overhead of coding around independent views so people hack in access to the controller access. This can be done by keeping an instance variable in the view, pointing to the controller, and assigning a reference to it after the view has loaded, or by overriding the init so you also pass a view controller, or lots of other ways. But before you do that think through the logic of why you want access to the controller from the view and see if there isn't a different way to do it.

Reuse UITableView code?

I have two view controllers, both of which contain table views. I would like to reuse the code because they are identical, and would like to keep things clean (as well as keep some data from the view controller). How can I go about doing this? Is it "allowed" so to speak, or is it frowned upon?
CustomTableView.h:
#interface CustomTableView : UITableView
#property (nonatomic, strong) NSString *someCoolString;
#property (nonatomic, strong) UIColor *superDuperColor;
#end
CustomTableView.m:
#import "CustomTableView.h"
#implementation CustomTableView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
// Initialization code
self.someCoolString = #"theString";
self.superDuperColor = [UIColor colorWithRed:48.0/255.0 green:32.0/255.0 blue:100.0/255.0 alpha:1.0];
}
return self;
}
#end
You should create a subclass of UITableView.

Application windows are expected to have a root view controller at the end of application

I have one problem and I've read following solution.This is my code
AppDelegat.h
#import <UIKit/UIKit.h>
#class ViewController;
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property(strong,nonatomic)ViewController *vobj;
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ViewController.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window= [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
self.vobj = [[ViewController alloc]initWithNibName:#"ViewController" bundle:nil];
self.window.rootViewController = self.vobj;
// Override point for customization after application launch.
self.window.backgroundColor = [UIColor whiteColor];
[self.window makeKeyAndVisible];
return YES;
}
and it gives an error Application windows are expected to have a root view controller at the end of application launch
This is my code
https://www.dropbox.com/s/y3gzur3tb032nz3/slide.zip
Applications are expected to have a root view controller at the end of application launch
Application windows are expected to have a root view controller at the end of application launch warning
Application windows are expected to have a root view controller at the end of application launch - even with all known issues fixed
and other link...
Thank You.
I found the source of the problem, and I made it work. It is deeper than it seems. The general problem is that on your "synthesised" line, you have also set the 'view' outlet to be synthesised. The 'view' is automatically associated on view creation with the 'view' property inside a view controller by iOS, and you just did an override on that association.
Change your line to this
#synthesize slide1,slide2,slide3,segmentview,segment1,segment2,switch1,newslider,lbl;
and you will be fine. The problem was that although your view controller was instantiated and assigned as a root view controller, its 'view' property was set to nil. You could see that using the debugger. iOS probably interpreted the nil view as an unassigned root view controller, hence the error you were seeing.
On a side note, might I give you some advice on how to save time, redundancy and make your code cleaner? Consider the following code:
#interface ViewController : UIViewController
{
// UISlider *slide1;
// UISlider *slide2;
// UISlider *slide3;
// UIView *newslider;
// UIView *segment1;
// UIView *segment2;
// UISegmentedControl *segmentview;
// UILabel *lbl;
// UISwitch *switch1;
//
}
#property (strong,nonatomic)IBOutlet UISlider *slide1;
#property (strong,nonatomic)IBOutlet UISlider *slide2;
#property (strong,nonatomic)IBOutlet UISlider *slide3;
#property (strong,nonatomic)IBOutlet UIView *newslider;
#property (strong,nonatomic)IBOutlet UIView *segment1;
#property (strong,nonatomic)IBOutlet UIView *segment2;
#property (strong,nonatomic)IBOutlet UISegmentedControl *segmentview;
#property (strong,nonatomic)IBOutlet UILabel *lbl;
#property (strong,nonatomic)IBOutlet UISwitch *switch1;
-(IBAction)btnchangecolor:(id)sender;
#end
Then, your view controller implementation would be like this:
#implementation ViewController
//#synthesize slide1,slide2,slide3,segmentview,segment1,segment2,switch1,view,newslider,lbl;
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
-(void)loadView {
[super loadView];
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
}
-(IBAction)btnchangecolor:(id)sender
{
if (_segmentview.selectedSegmentIndex==0) {
_segment1.backgroundColor=[UIColor colorWithRed:_slide1.value/255 green:_slide2.value/255 blue:_slide3.value/255 alpha:1.0];
}
else
{
_segment2.backgroundColor=[UIColor colorWithRed:_slide1.value/255 green:_slide2.value/255 blue:_slide3.value/255 alpha:1.0];
}
_lbl.backgroundColor=[UIColor colorWithRed:_slide1.value/255 green:_slide2.value/255 blue:_slide3.value/255 alpha:1.0];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
When you create a #property, modern Xcode versions produce a #property with that name, and sets the internal variable of a property like "myVar" automatically to "_myVar". Before automatic synthesizing feature, you could set the name of the internal variable by writing "#synthesize myVar = _myInternalVar", which would allow you to write "_myInternalVar = newValue" or "self.myVar = newValue" (you can still do that, by the way, if you wish). Seeing your code however, there seems to be some redundancy. You create iVars, and then, you create IBOutlets and then you synthesise the outlets with the name of the iVars. It's not an error, it's not even a warning, but you could save so much time by writing only the "#property" elements and not anything else, that I though I might just throw it as an option :)

How can we access values of an array added in a viewcontroller class, inside a different UIView class..?

viewcontroller.m has the following code
- (void)viewDidLoad
{
[super viewDidLoad];
self.array=[[NSArray alloc]initWithObjects:#"hi",#"hello", nil];
NSLog(#"%#",self.array);
view *view1=[[view alloc]init];
[view1 addSubview:self.view];
view1.viewController=self;
}
and there is another UIView class where I am trying to access the array :
the .h file :
#import <UIKit/UIKit.h>
#import "ViewController.h"
#class ViewController;
#interface view : UIView{
ViewController *viewController;
}
#property (nonatomic,retain)ViewController *viewController;
#end
and the .m file :
#import "view.h"
#import "ViewController.h"
#implementation view
#synthesize viewController;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
NSLog(#"%#",[viewController array]);
}
return self;
}
I checked in other posts of stackoverflow, and the passing of values was mentioned only between viewcontrollers; or the array was declared in the appdelegate and used in the classes(which I want to avoid).
The NSLog in the last code segment above gives null; so can you please help out in accessing the values of this array.
Thanks in advance..!!
You can achieve using this code in your ViewController
#import "view.h"
- (void)viewDidLoad
{
[super viewDidLoad];
NSArray *ary = [NSArray arrayWithObjects:#"7",#"5",#"3",#"2", nil];
view *v=[[view alloc] init];
[v initView:ary];
}
And in your view.h file :
#import <UIKit/UIKit.h>
#interface view : UIView
-(void)initView:(NSArray *)ary;
#end
And in your .m file :
#import "view.h"
#import "ViewController.h"
#implementation view
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
-(void)initView:(NSArray *)ary
{
NSLog(#"%#",ary);
}
/*
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
}
*/
#end
Log value will display this :
2013-02-20 20:11:52.731 SampleProject[9414:f803] (
7,
5,
3,
2
)
This line
view *view1=[[view alloc]init];
calls the desired initializer initWithFrame: before you set view1.viewController, so what's happening is that
NSLog(#"%#",[viewController array]);
actually calls
NSLog(#"%#",[null array]);
or (note that is pseudocode)
NSLog(#"%#",null);
What you'll want to do is to use view1.viewController after it is assigned. The best practice would be to make a custom constructor taking UIViewController* as a parameter and use it.
First thing you are calling the init method on the view and checking for viewController in the initWithFrame method which is never called. (But maybe you are calling the initWithFrame: from inside your init method with a default frame. :) ). Second, you are setting the viewcontroller property after you have called the init method, so your viewcontroller is still uninitialized in your initWithFrame method.
Third, instead of passing the whole of viewcontroller to your view to access the array (which kind of goes against MVC pattern), you could probably use just create an instance variable in your UIView subclass and pass just the array.
Then you could follow the answer given by Dilip, preferably using the setter method for setting the array. IMO.

Resources