In FatEditViewController, has a custom UIView.
FatEditViewController.h
#import <UIKit/UIKit.h>
#interface FatEditViewController
#property (weak, nonatomic) IBOutlet UIView *myview;
#end
In another UIViewController, I want to get the myview object.
I do like this:
FatEditViewController *vc = [self.storyboard instantiateViewControllerWithIdentifier:#"FatEdit"];
UIView *view = [vc myview];
NSLog(#"%#, %#", vc,view);
And the myview is null.
How to get the myview?
The problem here is that your myview gets instantiated only when your FatEditViewController's method viewDidLoad is called, which of course is called only when the view controller is presented on screen. One possible way to achieve what you need is to build myView programmatically (it wouldn't be an IBOutlet anymore), possibly by lazy initialising it in the getter. You would have to add the view to your FatEditViewController in the viewDidLoad method programmatically as well.
-(UIView *)myview
{
if(!_myview){
_myview = [[UIView alloc] initWithFrame:whateverFrameYouNeed];
//build the view
}
return _myview;
}
The property would have to be strong as well.
Related
I searched for answers like Get to UIViewController from UIView? and couple of other answers but was not successful.
My issue is that I have a button in UIView lets say class1 and when I click on that button I want to load another view class2 which is UIViewController, and as I don't get navigationController in class1 I am unable to load the class2 view.
Please help me with this.
Thanks,
In Advance.
In general UIViews should not contain any logic that triggers the flow of app. This is the job of UIViewControllers. It's just a way of making the design of your code better and more organized.
One way I often use is to use a delegate pattern in my custom UIViews. Here is s simple setup:
In your MyCustomView .h file:
#class MyCustomView;
#protocol MyCustomViewDelegate <NSObject>
#optional
- (void)myViewDidTapOnButton:(MyCustomView)myCustomView;
#end
#interface MyCustomView : UIView
#property (weak, nonatomic) id <MyCustomViewDelegate> delegate;
#end
In your MyCustomView .m file:
- (IBAction)didTapMyButton:(id)sender {
if ([self.delegate respondsToSelector:#selector(myViewDidTapOnButton:)]) {
[self.delegate myViewDidTapOnButton:self];
}
}
Then in your viewcontroller, which is presenting your view:
interface:
#interface MyViewController ()<MyCustomViewDelegate>
#property (weak, nonatomic) IBOutlet UIView *myCustomView;
and implementation:
- (void)viewDidLoad {
[super viewDidLoad];
self.myCustomView.delegate = self;
}
- (void)myViewDidTapOnButton:(MyCustomView)myCustomView {
... code for presenting viewcontroller ...
}
Note:
Even if you dont use the parameter myCustomView which is sent in this method, its a common pattern and good habit to always send the sender of the delegate as the first parameter.
This is also used a lot by Apple, e.g. in
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath;
Two cases :
If you are using storyboard then give your NavigationController a
storyboard id. And create an object of navigationController in your
custom UIView class.
If you have customized the app launching from AppDelegate create a
public property of your navigationController. From your UIView class create an object of appDelegate with [UIApplication sharedApplication].delegate. From this object access the navigationController property
When you have the navigationController object you can push your viewcontroller with:
[navigationController pushViewController:ViewController animated:YES];
First fill storyboard ID with "MyViewController", which is a String field that you can use to create a new ViewController based on that storyboard ViewController. And later access that view controller like this:
- (IBAction)buttonPressed:(id)sender{
MyCustomViewController *newvc = [self.storyboard instantiateViewControllerWithIdentifier:#"MyViewController"];
[self presentViewController:newvc animated:YES completion:nil];
}
When you click your button,you can do this:
YouViewController *yourViewController = [YouViewController new];
[self.view addSubView:yourViewController.view];
Hope to help you.
I have a scene in the storyboard with a Container View. As you may suppose, I want its associated UIViewController to load there another UIViewController as a child view controller, and to show its view as a subview.
So, in the parent UIViewController I defined the properties:
#property (weak, nonatomic) IBOutlet UIView *containerView;
#property (strong, nonatomic) MyChildViewController *childController;
Then, in the viewDidLoad method of the parent, I do:
if (self.childController == nil) {
self.childController = [[MyChildViewController alloc] init];
}
[self addChildViewController:self.childController];
self.childController.view.frame = CGRectMake(0, 0, self.containerView.frame.size.width, self.containerView.frame.size.height);
[self.containerView addSubview:self.childController.view];
[self.childController didMoveToParentViewController:self];
Both self.childController.view and self.containerView are not nil, but self.childController.view is not displayed when I run the app... What can I be missing?
Thanks
I finally noticed what I was overlooking... since I also created a scene in the storyboard for MyChildViewController, I needed to instantiate it from there.
I'm doing an Ipad app. Now I have 2 viewcontrollers, ViewController has a button1 which has a popover segue to the second viewcontroller(PopoverController). Then, the PopoverController has a button2, if I click the button2, I'll receive some UIImage from my server. I want to add fews subviews of UIImageView to the ViewController to display these images if I click the button2.
The button1 works well, the PopoverController can pop up as expected. BUT when I click the button2, nothing happend. I want to know how can I pass the data between 2 viewcontrollers and how to add subviews to another one.
Some codes relating to my problem:
ViewController.h:
#import <UIKit/UIKit.h>
#class PopoverController;
#interface ViewController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button1;
#property (strong, nonatomic) PopoverController *popoverController;
#end
PopoverController.h:
#import <UIKit/UIKit.h>
#class ViewController;
#interface PopoverController : UIViewController
#property (strong, nonatomic) IBOutlet UIButton *button2;
#property (strong, nonatomic) UIImage *tempImg;
#property (strong, nonatomic) ViewController *viewController;
- (IBAction)addsubviews:(id)sender;
#end
I can not just use [viewController.view addSubview:img1]; in the - (IBAction)addsubviews:(id)sender;method to addsubview. So someone can help me? :)
====1st update====
Someone suggest that I have to use - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender method. I have tried this one by control click the button2 and create a custom segue between button2 and ViewController. When I clicked the button2, it showed :
Terminating app due to uncaught exception 'NSGenericException', reason: 'Could not find a navigation controller for segue 'change'. Push segues can only be used when the source controller is managed by an instance of UINavigationController.'
So I'm wondering whether I should add a NavigationController. If so, what should I do?
====2nd update====
I use Paramasivan 's code now, and I found the way to call method from another viewcontroller. The problem now is the newly added subview in my viewcontroller doesn't show up. I guess I have to update my viewcontroller in order to make it visible.
in my - (IBAction)addsubviews:(id)sender; method, i invoke the method in ViewController by [self.viewController createSubViewWithImage:_tempImg];
so the method can be invoked when i click the button2, but the view of viewcontroller has nothing changed.
Add this in - (void)viewDidLoad,
self.popoverController = [[PopoverController alloc] init];
self.popoverController.viewController = self;
Make sure that in no other places, you are setting self.popoverController = ....
Do NOT add anything like self.viewController = ... in popovercontroller class. And you dont have to do self.viewController.popoverController = self; as well. Just remove these lines if you already have it.
Once these are done, make sure that you are displaying self.popoverController only in the popover and you are not creating a new object for popoverController class there. So if these are fine, you can use any approach you want for passing the image from popoverController class to viewController class.
as you mentioned in your comment you can use [self.viewController createSubViewWithImage:_tempImg]; in your popovercontroller class.
Update:
If you are doing via storyboard, you need to set this in prepareForSegue method and you dont have to create self.popoverController at all. Remove that part in your case. You can follow the procedure mentioned here to set up a custom segue and implement prepareForSegue method to pass the object. Source: On storyboards, views and passing data along
Set the name of segue in storyboard to "CustomSegue"
Implement prepareForSegue method
Inside the method, check if name of segue matches "CustomSegue" and then set the viewController in the popoverController object there as,
Try,
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
if ([segue.identifier isEqualToString:#"CustomSegue"]) {
PopoverController *popoverController = [segue destinationViewController];
popoverController.viewController = self;
}
}
After doing this, you need to call [self.viewController createSubViewWithImage:_tempImg]; in your popoverController class.
Check out the Communicating with Objects doc, there are several ways to do what you want.
In ViewController.h add the following
-(void)createSubViewWithImage:(UIImage *)imageDownloaded {
UIImageView *imageViewTemp = [[UIImageView alloc] initWithImage:imageDownloaded];
[self.view addSubView:imageViewTemp];
}
In PopoverController.h add the following
#property (nonatomic, retain) ViewController *viewControllerPassed;
And after image downloaded, call the following in PopoverController.h
[viewControllerPassed createSubViewWithImage:imageDownloaded];
You can pass data using the below method:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"ViewController2"])
{
ViewController2 *v2VC = [segue destinationViewController];
v2VC.yourData = self.someStuff;
}
}
In my main view controller, I have a button that calls a popover. Since the popover has it's own view controller, its buttons call methods in the popover's view controller. But how would I call a method from the main view controller?
I tried this. In the popover view controller I added a property in the .h
#class ViewController;
#interface PopoverContent : UIViewController <UITextFieldDelegate>
...
#property (strong, nonatomic) ViewController *parentView;
In my popover implementation I did this in viewDidLoad:
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
In ViewController I have a method called generateHash so I tried
[parentView generateHash];
But I get the error:
No visible #interface for 'ViewController' declares selector 'generateHash'
Any idea what I'm doing wrong? Thanks
remove this line, don't want to create a new instance of view controller
self.parentView = [[ViewController alloc] initWithNibName:nil bundle:nil];
change this line
#property (strong, nonatomic) ViewController *parentView;
to this, so your parent view pointer is of the right class type
#property (weak) ParentView *parentView;
now inside parent views .m file
- (void) createPopup
{
PopoverView *popoverV = [[PopoverView alloc] init];
popoverV.parentView = self;
//And some command to show your popup, addSubview, or presentModal, or whatever
}
Then in PopoverView.m file, you can call methods of the parentView like so
[self.parentView SomeMethod];
This is a good place to use a delegate protocol. In the PopoverContent.h, add something like this:
#protocol PopoverContentDelegate : NSObject
- (void) method1;
#end
Naturally, you can have more than one method, and the method(s) can return values and take parameters like any other method. Also, in the same file, add property called delegate. (Technically, it can be called anything, but everyone who looks at your code will know exactly what you're doing if you call it delegate.)
#interface PopoverContent
#property (weak) id<PopoverContentDelegate > delegate;
//other properties and methods
#end
Finally, in your "main" view controller's .m file, import PopoverContent.h file and set the delegate to self. Also implement method1 to do whatever you need it to do.
//Create the view controller
myPopoverContentController.delegate = self;
//Create the popover with the view controller.
Now, in PopoverContent controller, you can call method1 on the delegate wherever you need to.
[delegate method1];
First, to answer your question, you probably have to define the generateHash method in your ViewController.h file.
Second, I'd suggest that your design approach is not optimal. The generateHash method probably needs to be in another file that can be called from both your ViewController and Popover content controller. For example consider another objective-c .h/.m pair "MyHashMethods":
MyHashMethods.h
+ (void)generateHash;
MyHashMethods.m
+ (void) generateHash
{
// hash code
}
This would allow you to just include MyHashMethods.h in whatever view controllers you need and then call
[MyHashMethods generateHash];
when you need it.
I'd like to develop for iOS5 and have a storyboard...
I've just created UIView with UIViewController on the storyboard.
I have added a lot of other UIButtons and labels and creates outlets to VC.
I would like to use this view with it's viewcontroller 3 times on a single parent view.
How is it possible? I dont want to copy "a lot of other UIButtons and labels" ...
Maybe i should create this view out of storyboard in separate XIB? How will i use XIB in storyboard?
UPDATE:
Thanx you, Juzzz - your solution works perfect:
You have to create 2 custom viewControllers (one for each view in your story board.
example:
#interface GraphCollectionViewController : UIViewController
#interface GraphViewController : UIViewController
To connect them you can use something called: UIViewController containment
For your GraphCollectionViewController create 3 outlets for your UIViews. Then create 3 properties of GraphViewController's (or an array, what you want) and initialize them in the view did load.
#property (strong, nonatomic) IBOutlet UIView *topView;
#property (strong, nonatomic) IBOutlet UIView *middleView;
#property (strong, nonatomic) IBOutlet UIView *bottomView;
#property (strong, nonatomic) GraphViewController *topGraphViewController;
#property (strong, nonatomic) GraphViewController *middleGraphViewController;
#property (strong, nonatomic) GraphViewController *bottomGraphViewController;
...
//top graph
self.topGraphViewController = [storyboard instantiateViewControllerWithIdentifier:#"GraphViewController"]; //init with view from storyboard
self.topGraphViewController.view.frame = self.topView.bounds; //set frame the same
[self.view addSubview:self.topGraphViewController.view];
[self addChildViewController:self.topGraphViewController];
[self.topGraphViewController didMoveToParentViewController:self];
//middle graph
self.middleGraphViewController = [storyboard instantiateViewControllerWithIdentifier:#"GraphViewController"]; //init with view from storyboard
self.middleGraphViewController.view.frame = self.middleView.bounds; //set frame the same
[self.view addSubview:self.middleGraphViewController.view];
[self addChildViewController:self.middleGraphViewController];
[self.middleGraphViewController didMoveToParentViewController:self];
//bottom graph
self.bottomGraphViewController = [storyboard instantiateViewControllerWithIdentifier:#"GraphViewController"]; //init with view from storyboard
self.bottomGraphViewController.view.frame = self.bottomView.bounds; //set frame the same
[self.view addSubview:self.bottomGraphViewController.view];
[self addChildViewController:self.bottomGraphViewController];
[self.bottomGraphViewController didMoveToParentViewController:self];
I think you get the point. For more understanding this example .
You can create a new view class (with it's own .h, .m files) and base this on the ViewController you created yourself.
So as for code:
#interface ViewController : UIViewController
Let's say the above is your original ViewController that you want to use in other places.
This has buttons, labels, etc. in it.
When you create a new view class base it on your own instead of UIViewController class like this:
#interface MyNewController : ViewController
Now that MyNewController is based on ViewController you created earlier, it should have it's buttons, labels, etc. you created as well.
Edit: You might also have to change your view's base class in the storyboard.
Click on the view you want to change, look at the right hand side attributes window, under Custom Class choose your own controller, in this case ViewController.