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.
Related
I am planning to create two UIViewController. Basically, there are some buttons in one of the two view controllers. When I press one of these buttons, I want to trigger some action in another view controller. Are there some ways to make it possible?
There are so three possible ways to do that,
Using NSNotificationCenter, Using Delegates and the last one is using Blocks, The first option is easy to learn.
Add the observer in to FirstViewController
Post notification in SecondViewController (when user clicks the button)
For reference follow this link
Hope this helps
Yes, you can use a delegate for that.
For example, you may have this ViewControllers.
//FirstViewController.h
#protocol ProtocolName <NSObject>
- (void)doSomething;
#end
#interface FirstViewController
#property (nonatomic, strong) id<ProtocolName> delegate;
#property (weak, nonatomic) IBOutlet UIButton *button;
- (IBAction)action:(id)sender;
#end
Here we need a protocol, who defines one or more methods to be implemented in any Object who implement this protocol. And also we need a delegate, that is an object who implements this protocol (in this case called "ProtocolName", but you can name it as you wish).
And then a second ViewController
//SecondViewController.h
#import "FirstViewController.h" //Need this to reference protocol
#interface SecondViewController <ProtocolName>
#end
With we are saying that SecondViewController will implement ProtocolName protocol, so in his .m file we need to do this.
//SecondViewController.m
- (void)doSomething{
//Do something
}
And here comes the magic.
Let's say that when you tap the button in FirstViewController it triggers the doSomething method in SecondViewController. So, you need to do something like this.
//FirstViewController.m
//...
SecondViewController secondVC = [[SecondViewController alloc] init];
this.delegate = secondVC; //DON'T FORGET THIS
//...
- (IBAction)action:(id)sender{
[this.delegate doSomething];
}
And this is the delegate pattern.
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.
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];
}
I have a UIViewsubclass. I am not able to create an instance of another View Controller in this UIView class, so that i can access the variables set in my UIView subclass in this View Controller? Can anyone guide me on this
#import <UIKit/UIKit.h>
#import "DirectoryFormViewController.h"
#class NIDropDown;
#protocol NIDropDownDelegate
- (void) niDropDownDelegateMethod: (NIDropDown *) sender;
#end
#interface NIDropDown : UIView <UITableViewDelegate, UITableViewDataSource>
{
NSString *animationDirection;
UIImageView *imgView;
DirectoryFormViewController *dict; // i am not able to create this
}
#property (nonatomic, retain) id <NIDropDownDelegate> delegate;
#property (nonatomic, retain) NSString *animationDirection;
-(void)hideDropDown:(UIButton *)b;
- (id)showDropDown:(UIButton *)b:(CGFloat *)height:(NSArray *)arr:(NSArray *)imgArr: (NSString *)direction;
#property(nonatomic)int countryID;
#end
My DirectoyFormViewController:
#import <UIKit/UIKit.h>
#import "NIDropDown.h"
#interface DirectoryFormViewController : UIViewController<DropDownListDelegate,CLLocationManagerDelegate,UISearchBarDelegate,UITextFieldDelegate,NIDropDownDelegate>
#property(nonatomic)NSMutableDictionary *countryName;
#property(nonatomic,copy)NSMutableDictionary *sortName;
#property(nonatomic,copy)NSMutableDictionary *resultName;
#end
I want to set countryName,sortName and resultName in my NIDropDown
Thanks
Here's the short answer: you should never, ever, not for any reason create an instance of a view controller inside of a UIView. So the fact that you have not been successful so far is a good thing.
The Apple way of development on iOS is to use the Model, View, Controller design pattern. In MVC, the controller controls the models and the views and mediates communication between the two... not the other way around.
My suggestion is that you read the link and fully understand it before moving forward with development. With an understanding of the topics covered you'll never do things like have a UIView that's a table view delegate / datasource (because by doing that, your view is aware of the model, and that breaks MVC), and you'll hopefully never try to do things like create a UIViewController in a UIView.
I have a container view controller with 3 child UIViewController subclasses (added with addChildViewController). I want one of my child view controllers to do something when something is dropped from my container view controller onto it. I'm having trouble grasping how this communication should happen. If I try making a delegate, I get an error in my child view controller because I would both subclasses to import each other.
It sounds like you're having a problem compiling your app because of mutual .h files importing each other, right?
Edit: upon reading your question again, I'm not 100% clear on which view controller needs to call which other one. If I mixed up the
roles of parent and child view controller in my solution, just switch
them. The techniques below let you communicate between any two view
controllers (parent and child, sibling and sibling, etc.)
There's a number of ways to handle this. If you want to stay with a delegate pattern, you could simply rewrite the header to avoid the #import in one of the .h files:
ParentViewController.h:
#import "ChildViewController.h"
#interface ParentViewController: UIViewController {
#private
ChildViewController* childVc;
}
- (void) doSomething;
ChildViewController.h
#class ParentViewController; // NOT #import!
#interface ChildViewController: UIViewController {
#private
ParentViewController* parentVc;
}
ChildViewController.m
#import "ParentViewController.h"
This should avoid the circular dependency that keeps your app from compiling.
Now, although the above works, I might choose another solution, for the sake of cleanliness. Use a protocol. The parent can implement the protocol and then the child only needs to have a delegate that implements the protocol:
#import "MyProtocol.h"
#interface ParentViewController: UIViewController<MyProtocol> {
}
- (void) doSomething;
In MyProtocol.h:
#protocol MyProtocol
- (void) doSomething;
#end
Then in ChildViewController.h
#import "MyProtocol.h"
#interface ChildViewController: UIViewController {
#private
id<MyProtocol> delegate;
}
#property (nonatomic, assign) id<MyProtocol> delegate;
And in ChildViewController.m:
[delegate doSomething];
Or, you could avoid using delegates altogether, and communicate between the controllers using NSNotificationCenter, which decouples them a bit, and avoids your compiler circularity (bidirectional dependency).
Here are the Apple docs on NSNotificationCenter
Couldn't you just go:
MyChildViewController *myChildViewController = (MyChildViewController *)[self.childViewControllers objectAtIndex:0];
[myChildViewController doWhatever];
?
That should let you message the child view controller at the first index of the array childViewControllers (which is a property on UIViewController).