Composing iOS Views - ios

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.

Related

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];
}

iOS MVC implementation with custom views

When the views are simple, their IBActions and IBoutlets are in viewcontroller, viewcontrollers assigns respective models to be loaded and viewcontroller get notified when models are prepared.
As My project contains lot of custom views for each viewcontroller, I want to implement actions in custom view itself and set data from controller (ViewController).
I should be able to use the same controllers and models for both iPhone and iPad where only UI changes.
I am concerned about how to pass data from view to viewcontroller and displaying data back on view when model changes?
Can anyone please suggest me to pass data between views <---> viewcontroller (controller) <---> model?
To do this I use Delegate design-pattern. It looks like this :
MyView.h
#protocol MyViewDelegate <NSObject>
- (void)customViewDidSomething;
#end
#interface MyView : UIView
#property (nonatomic, assign) id<MyViewDelegate> delegate
#end
MyView.m
- (void)userDidSomething {
[_delegate customViewDidSomething];
}
MyViewController.h
#import "MyView.h"
// ViewController has to implement the protocol
#interface MyViewController <MyViewDelegate>
#property (nonatomic, retain) IBOutlet MyView myView;
MyViewController.m
- (void)viewDidLoad { // Set the delegate somewhere
_myView.delegate = self
}
- (void)customViewDidSomething {
// Ok VC is aware that something happened
// Do something (tell subview to do something ?)
}
Instead of using different custom views, try using a UIViewController and then use the viewcontroller's view to display your UI. Also, this will also ensure that you will be able to communicate between the views and controller efficiently without confusion.

iOS change UIViewController

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

how to define action in one view controller and call them anywhere [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 9 years ago.
Improve this question
What I have is three button at the top of my app.
Those three buttons will always appear on all view controller.
When I click button, respective action will be taken.
As we have this in all viewcontroller, what I am planning is I will have method defined somewhere which I will call on clicking this button.
I am doing this because I will have method written once and call them anywhere.
ELse I had to write method for all view controller and if there are changes later, I will have to do for all view controllers.
Any idea how can I achieve the same?
What I want to do is define some method in one UIViewController and call that method in many different ViewController.
Consider creating your own container view controller and add your true content view controllers as children (using addChildViewController:). Then your container view controller can manage the global buttons and their actions without affecting any of the actual content view controllers.
Another technique aside from having a container view controller is to have each of your viewControllers subclass another viewcontroller in your project.
#interface MainViewController : UIViewController; // implements your buttons and their actions
#interface OneViewController : MainViewController;
#interface TwoViewController : MainViewController;
#interface ThreeViewController : MainViewController;
Although subclassing is an option, using Objective-C categories will give you greater flexibility.
Create a category that extends UIViewController:
#interface UIViewController (SharedLogic)
- (void)didPressFirstButton:(id)sender;
- (void)didPressSecondButton:(id)sender;
- (void)didPressThirdButton:(id)sender;
#end
Whatever classes specifies the target/action pair for each button will need to #import this category, but otherwise you should get the behavior you want.
You could do this with delegates...here's a nice explanation of this:
How do I set up a simple delegate to communicate between two view controllers?
What I did is as below and it's working.
Created UIViewController as TopBarViewController which is common for all.
TopBarViewController.h
#import <UIKit/UIKit.h>
#interface TopBarViewController : UIViewController
- (IBAction)clickedSetting:(id)sender;
#end
TopBarViewController.m
#import "TopBarViewController.h"
#interface TopBarViewController ()
#end
#implementation TopBarViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)clickedSetting:(id)sender {
NSLog(#"clickedSetting-TopBar");
// do your action here
}
#end
Now when I click button in MyViewController, I call action as below.
- (IBAction)clickedSetting:(id)sender {
NSLog(#"self-clickedSetting");
topBarViewCon = [[TopBarViewController alloc] init];
[topBarViewCon clickedSetting:nil];
}
I have #property (nonatomic, retain) TopBarViewController *topBarViewCon; in .h file & synthesize this property in .m file.
Is this right way is this is wrong way?
Consider using NSNotificationCenter to broadcast UI events to different places in your codebase.

Sending data from a viewcontroller to another

I am struggling to make a simple thing (at least I think it's simple) but I just can't do it!
I will try to explain a little bit.
It's an app which displays information. When the user is inside a view, he can click on a button, which displays a popoverview, where he can choose which information he wants to know.
Actually, I can't create an action that changes the UILabel text I created in the main view when the user clicks on the popoverview's buttons.
Anyone has any idea?
Just for you to know: the main view I created a class for it, and also for the popoverview. Although, the popover view I created its design in a XIB file (I don't know if this is important, that's why I am putting this).
Well, I hope you guys were able to understand my question.
Thanks in advance.
Fernando.
Just create a property from the viewcontroller and access it from the consumer (other viewcontroller )
You will have to use delegation in order to see changes in the main view when you are making different actions inside the popover. First, you need to create a protocol inside your popover controller header file:
#import <Foundation/Foundation.h>
#class MyPopoverController;
#protocol MyPopoverDelegate
- (void)valueChanged:(NSString*) newVal;
#end
#interface MyPopoverController: UIPopoverController
#property (weak) id<MyPopoverDelegate> delegate;
#end
Then in .m you implement it like this:
- (void) someActionOccured
{
if([self.delegate respondsToSelector:#selector(valueChanged:)]){
[self.delegate valueChanged:valueYouWantToSendBack];
}
}
Remember that in your main class you have to implement MyPopoverDelegate protocol:
#interface MainViewController: UIViewController <MyPopoverDelegate>
And when you instantiate your popover controller:
/*
** inside MainViewController.m
*/
// remember to assign it's delegate
MyPopoverController *popoverController = [MyPopoverController alloc] init];
popoverController.delegate = self;
Also, you'll have to implement the protocol's method:
/*
** inside MainViewController.m
*/
- (void)valueChanged:(NSString*) newVal
{
// process the string and display it where you need it
}

Resources