iOS change UIViewController - ios

In my app I have a gradient as a background. This gradient is made programmatically. The way that I now use this is like this:
I have a UIViewController which needs to display the gradient and in that class I do this :
- (void) viewWillAppear:(BOOL)animated{
[super viewDidAppear:animated];
[Gradient gradientInViewController:self];
}
This ain't so bad but this needs to be done in all the classes which isn't very good programming. What I want is instead of making a class a UIViewController, I want it to be a GradientViewController which is a subclass of UIViewController and in this class I will handle everything.
So my question is how do I do this? I think this has to be done through categories? But I can't figure out how to get the image on the screen. Should this be done in viewWillAppear or something?

Make a GradientViewController which handles the gradient drawing
#interface GradientViewController : UIViewController
#end
#implementation GradientViewController
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
[Gradient gradientInViewController:self];
}
#end
Then inherit all your other controllers from that
#interface YourViewController : GradientViewController
#end
#implementation YourViewController
- (void) viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
// no need to do anything
}
#end
It's more flexible to do it through helper classes or categories though, even if there's a bit of repetition.
Note as pointed out by Nguyen Duc, that you are calling [super viewDidAppear:] for viewWillAppear: which is wrong, I edited the answer.

Why not use Interface Builder and have a xib that knows how to load your image for you automatically?

You can get this behaviour "for free" throughout your app simply by subclassing UIViewController:-
#interface GradientViewController : UIViewController
#end
and implementing your gradient code in viewDidLoad: as such:-
- (void)viewDidLoad
{
[super viewDidLoad];
[Gradient gradientInViewController:self];
}
Then just use GradientViewController instead of UIViewController everywhere you need your gradient.
Alternatively you can use a category. This can be useful because you can use categories for "themes" for your app, configuring various UI elements of your UIViewControllers. But I'd set up a category on UIViewController specifically to set your gradient, and then call the category method in every view controller that needs it - do not attempt to do it by overriding viewDidLoad: or viewWillAppear: in a category.

Just create a new view controller (let's call it GradientViewController for example), put the same code that draws the gradient into its view viewWillAppear, then make all your view controllers a subclass of the view controller by replacing
#interface SomeViewController : UIViewController
with
#interface SomeViewController : GradientViewController

Related

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.

Where to set delegate = self? Or should I just use a different design pattern?

EDIT: edited for clarity
Disclaimer: I'm new and pretty bad. But I have tried very hard and read lots of stuff to figure this out, but I have not...
I think my whole delegate pattern would work, except I can't figure out how to set the delegate property of ViewController to self in the MatchLetter class. The reason is because I can't figure out how to call code there. It's not a view controller, so viewDidLoad or prepareForSegue won't work.
This is what I've got:
ViewController.h
#import <UIKit/UIKit.h>
#class ViewController;
#protocol letterMatchProtocol <NSObject>
- (BOOL) isLetterMatch:(char) firstLetter;
#end
#interface ViewController : UIViewController
#property (nonatomic, weak) id <letterMatchProtocol> delegate;
#end
ViewController.m
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
char c = 'a';
// This is the method I want to delegate to MatchLetter, to have a BOOL returned
BOOL returnValue = [self.delegate isLetterMatch:c];
}
#end
MatchLetter.h
#import <Foundation/Foundation.h>
#import "ViewController.h"
#interface Delegate : NSObject <letterMatchProtocol>
#end
MatchLetter.m
#import "MatchLetter.h"
#implementation Delegate
// this is the code I think I need to run here, to set the delegate property...
// ViewController *viewController = [ViewController new];
// viewController.delegate = self;
// ... so that isLetterMatch can be run here from ViewController.m
// But I don't know where to put this code, or how to get it to run before the ViewController
// especially since there are no segues or views to load.
- (BOOL) isLetterMatch:(char)firstLetter {
if (firstLetter == 'a') {
return YES;
}
else {
return NO;
}
}
#end
Can somebody please tell me the best way to proceed? Thanks for reading
You asked "Where to set delegate = self? Or should I just use a different design pattern?".
Answer: Don't. An object should never be it's own delegate.
Your code is quite a mess.
Don't name a class "Delegate". A delegate is a design pattern. The whole point of a delegate is that any object that conforms to a particular protocol ("speaks the language") can serve as the delegate. You don't need to know what class of object is serving as the delegate, but only that it speaks the language you need.
An analogy: When you call the operator, you don't care who is working the operator desk. You don't care about his/her gender, religion, ethnic background, how tall they are, etc. You just care that they speak your language.
Likewise, when you set up a delegate, it doesn't matter what type of object gets set as the delegate. All that matters is that the object that is the delegate conforms to the protocol for that delegate.
A table view can have ANY object serve as it's delegate, as long as that object conforms to the UITableViewDelegate protocol. You usually make you view controller be the table view's delegate, but you don't have to. You could create a custom class that manages your table views, and have it be the delegate. There is no "TableViewDelegate" object class. There is instead a UITableViewDelegate protocol, and any object that conforms to the protocol can act as a table view's delegate.
Edit: Your question is confusing. I think what you're proposing is that your Delegate class would create a view controller and make itself the delegate for the view controller.
If that's what you are talking about, your thinking is backwards. The view controller is using the Delegate class as a helper class. Any given instance of a view controller class can create an instance of the Delegate class and set it as it's delegate if it desires. You might have 3 instances of ViewController at one time, each with it's own instance of your Delegate class.
Thus, the ViewController object is the one that should create and set up an instance of Delegate if it needs one:
- (void) viewDidLoad;
{
self.delegate = [[Delegate alloc] init];
//other setup here
}

Accessing interface builder object from view controller

I'm completely new to Objective-C, XCode, and iOS development and I'm trying to figure out how to run certain code at startup, after all UI views and controls have been instantiated. I have a generic NSObject that I've added through interface builder by dragging it into my view controller scene. It's defined as follows:
#import <Foundation/Foundation.h>
#interface Controller : NSObject {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
}
-(IBAction)sliderChanged:(id)sender;
#end
I need to run sliderChanged on initialization. I've tried the following way:
#import "Controller.h"
#implementation Controller
-(id)init {
self = [super init];
if (self){
[self sliderChanged:nil];
}
return self;
}
// More code here
But both my slider and label are nil when this is called. I understand there's a viewDidLoad method within the ViewController class which may be what I need, but I'm not sure how to access the instance of my Controller class (which seems to be instantiated somewhere behind the scenes by the interface builder) from within that method. Should all of this code simply be moved to the ViewController itself? That would seem to make sense, but the design above is what we've been instructed in class, so I'm not really sure how to go about doing this.
After the XIB/Storyboard loader finishes loading all the objects and wiring them up, it sends awakeFromNib to every object that was instantiated from the XIB. So try adding this to your Controller class:
- (void)awakeFromNib {
[super awakeFromNib];
[self sliderChanged:nil];
}
You can find more information in the NSObject UIKit Additions Reference and “The Nib Object Life Cycle” in the Resource Programming Guide.
HOWEVER, if you created Controller as a top-level object, and you didn't connect any outlets to it, then nothing references it after the XIB loader finishes with it, so the system will deallocate it again. That's probably not what you want, so you should connect an outlet in your view controller to the Controller. If you do that (and let's say the outlet is named controller), then you can access it in viewDidLoad in your view controller class:
#import <UIKit/UIKit.h>
#import "Controller.h"
#interface ViewController : UIViewController {
IBOutlet Controller *controller;
}
#end
Implementation:
- (void)viewDidLoad {
[super viewDidLoad];
[self.controller sliderChanged:self];
}

execute a method in all the ViewControllers

Maybe it is not possible but here is my question:
I need to execute the same method on all the ViewControllers, precisely on the viewDidLoad or on the viewDidAppear methods.
Is there any way on the AppDelegate or somewhere of doing this apart from calling this function from every single controller?
Try this,
Create a BaseViewController
#interface BaseViewController : UIViewController
and create all other ViewControllers by subclassing BaseViewController
#interface AViewController : BaseViewController
and do ypu stuff in viewDidLoad or on the viewDidAppear of BaseViewController
Here is a simple approach to do this. Follow OOPS & make the use of inheritance like this.
Make a baseViewController & add two methods for example.
btBaseViewController.h
#interface btBaseViewController : UIViewController
#end
-(void)renderPadUI;
-(void)renderPhoneUI;
Provide a no-op definition for these two methods.
btBaseViewController.m
-(void)renderPhoneUI {
// to be overridden by derrived classes
}
-(void)renderPadUI {
// to me overridden by derrived classes.
}
And in viewDidLoad do something like this
if(IS_IPAD) {
[self renderPadUI];
}else{
[self renderPhoneUI];
}
Now Sub class your ViewControllers from this btBaseViewController class as
btHomeViewController.h
#interface btHomeViewController : btBaseViewController
#end
Now if you do not provide the definition of methods renderPhoneUI and renderPADUI btHomeViewController the base version will be executed.
Thats simple.
I'm guessing making a UIViewController subclass that does what you do and make every controller of your app of that new subclass type.
MyUIViewController.h
#interface MYUIViewController : UIViewController
#end
MYUIViewController.m
#implementation MYUIViewController
- (void)viewDidLoad
{
[super viewDidLoad];
[self myMethodCall];
}
And then, every UIViewController that needs to execute this method, should be of MYUIViewController type.
You can define the method in the app delegate or you can make a subclass of a UIViewController

Composing iOS Views

I've created a view with a logout button and I'm trying to make that a subview of another view. The logout button view has a xib and a controller associated with the xib.
How do I make it so that this view/controller is a part of my other view?
The way I've done this before is by having a view that draws itself programmatically, drawing that view in the interface builder as part of another view and changing the class for that view. As I want that view to respond to methods, I made it have a protocol and then made the controller it was a subview of implement that.
Is that the only way to do it? Or is there a way such that I have an independent controller for my logout view that I can just 'drop in' into other views, because the drawback of the other method is that every view that wants to use this subview has to implement the protocol, even if that method is going to be the same in every view.
Create a superclass to abstract the logout behavior. Then, each UIViewController that supports the logout should subclass that superclass. In the superclass, provide the method for logout.
This approach will enable you to either simply hook up UIControls in Interface Builder to the common IBAction in the superclass, or alternatively, even add specific customization in the subclass before invoking the superclass method.
Here's one possible example:
LogoutViewController.h
#import <UIKit/UIKit.h>
#interface LogoutViewController : UIViewController
-(void)performLogout;
#end
LogoutViewController.m
#import "LogoutViewController.h"
#interface LogoutViewController ()
#end
#implementation LogoutViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (void)performLogout
{
//do logout code
}
- (IBAction)logout:(id)sender
{
[self performLogout];
}
#end
SomeOtherViewController.h
#import <UIKit/UIKit.h>
#import "LogoutViewController.h"
#interface SomeOtherViewController : LogoutViewController
#end
SomeOtherViewController.m
#import "SomeOtherViewController.h"
#implementation SomeOtherViewController
- (void)viewDidLoad
{
[super viewDidLoad];
}
- (IBAction)mySpecificLogoutButtonPressed:(id)sender
{
self.title = #"Good bye";
// do other code specific to logging out from this UIVC
[super performLogout];
}
#end
You can use NSNotificationCenter for this. So you can post the notification on logout button action. You can check the documentation.
Hope this helps.

Resources