as you can read in the title, I want to display an UIView from one UIViewController in another UIViewController.
The reason for this is, that I want to design the UIView in an extra UIViewController in the storyboard, but use it as an overlay view in my MainViewController on which the app mainly runs.
I tried the following:
In the MainViewController I created an instance of the second UIViewController and saved its view in the UIView i called overlayView.
UIViewController* rvc = [self.storyboard instantiateViewControllerWithIdentifier:#"overlayView"];
self.overlayView = rvc.view;
After that step, I thought I simply add it as a Subview, but sadly it didn't work.
[self.view addSubview:self.overlayView];
Is there anything missing or should I try something completely different ?
Thanks in advance :)
EDIT: Now theres the problem, that an Error called BAD_ACCESS occurs by pressing a button of the overlayView.
I created a ViewController, whose views alpha I regulated to a value of 0.9. On this view I added a simple button up to now. In my MainViewControllers viewDidLoad Method I used the following code to initialize the "new" ViewControllers view:
UIStoryboard* st = [UIStoryboard storyboardWithName:#"Main_iPhone" bundle:nil];
UIViewController* vc = [st instantiateViewControllerWithIdentifier:#"overlayView"];
self.overlayView = vc.view;
[self addChildViewController:vc];
self.overlayView.alpha = 0.0;
As a next step I "called" the overlayView to appear by pressing a button on the MainViewController and animated the alpha for a smooth fadeIn:
[self.view addSubview:self.overlayView];
[UIView animateWithDuration:1.3 animations:^{
self.overlayView.alpha = 0.9;
}];
Now if I press the button on the overlayView I want it to call a method of the MainViewController, which is doing some stuff and lets the overlayView disappear. For this case I used a NSNotificationCenter.
But by pressing this button (on the overlayView), the following error occurs in main.m:
Thread 1: EXC_BAD_ACCESS (code=1, address=0x128a60cd0)
First I thought, the problem was caused by the NSNotificationCenter. So I tried to call the method using Delegation. But it didn't help.
Delegation Code:
OverlayViewController.h
#class OverlayViewController;
#protocol OverlayViewControllerDelegate
- (void) setToNormalAction;
#end
#interface OverlayViewController : UIViewController
#property (weak, nonatomic) id <OverlayViewControllerDelegate> delegate;
#property (strong, nonatomic) IBOutlet UIView *overlayView;
- (IBAction)buttonTouchUpInside:(id)sender;
#end
Button Method:
- (IBAction)buttonTouchUpInside:(id)sender
{
[self.delegate setToNormalAction];
}
In the MainViewController.h I implemented the created "OverlayViewControllerDelegate" as known and in .m I used the declared method to fadeOut the overlayView and do some other stuff.
Do you have an idea to resolve this ?
Thanks in advance
Try...
UIViewController* rvc = [self.storyboardinstantiateViewControllerWithIdentifier:#"overlayView"];
self.overlayView = rvc.view;
[self addChildViewController:rvc];
[self.view addSubview:self.overlayView];
You could just design it as a view in a nib then there's no need for an extra VC just to have somewhere to put it, and all the hassles that is going to bring.
There's nothing that says you can't continue to use nibs in addition to a having a storyboard.
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.
Goal: to switch views when you click the button.
What I did:
Open a single view application.
File -> New -> File -> Cocoa Touch & Objective -C. Create
PlayViewController and GMViewController, both subclass
UIViewController.
Went to mainstoryboard and from the object library, I dragged two
View Controllers, with PLayViewController and GMViewController as
their STORYBOARD IDs.
IN ViewController.h I have these codes:
.
#import <UIKit/UIKit.h>
#class PlayViewController;
#class GMViewController;
#interface ViewController : UIViewController
#property (strong, nonatomic) PlayViewController *playViewController;
#property (strong, nonatomic) GMViewController *gmViewController;
- (IBAction)toPlay:(id)sender;
- (IBAction)toGM:(id)sender;
#end
Basically, here I created two buttons. one will go to PlayView, the other to GMView.
5. In my ViewCotroller.m, I have these:
#import "ViewController.h"
#import "PLayViewController.h"
#import "GMViewController.h"
- (IBAction)toPlay:(id)sender {
UIStoryboard *storyboard =[UIStoryboard storyboardWithName:#"Main" bundle:nil];
PlayViewController *playController =[storyboard instantiateViewControllerWithIdentifier:#"PlayViewController"];
self.playViewController = playController;
[self.view removeFromSuperview];
[self.view insertSubview: self.playViewController.view atIndex:0];
}
- (IBAction)toGM:(id)sender {
//still blank, waiting to fix the play button.
}
Problem is when I click the play button the screen goes black. Please tell me what I missed. Thanks
As per your code, you are first removing self.view and then you are trying to insertSubview on self.view thats way it is not working for you so change your code with
first insret self.playViewController.view to self.view and then it remove from super view such like,
[self.view insertSubview: self.playViewController.view atIndex:0];
[self.view removeFromSuperview];
Hope that my suggestion will be work for you.
Why UISearchBar does not get Focus and keyboard does not appear when UIViewController is pushed to navigation controller for the 2nd time? It seems as no element on the whole view has a focus.
MY USE CASE: I have 2 View Controllers: A and B. Transition is A->B. A, B are pushed to navigation controller.
When I show B for the first time focus is set to the searchbar and keyboard appears.OK.
Then I go back to the A. And again from A->B. Now for the second time there is no focus and keyboard does not appear. It is WRONG.
MY CODE: On the B UIViewController IBOutlet connection to the searchbar is created:
#property (weak, nonatomic) IBOutlet UISearchBar *mySearchBar;
Delegate of the mySearchBar is also set.
I set focus on the searchbar in B UIViewController in viewDidAppear method:
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
[self performSelector:#selector(setCorrectFocus) withObject:NULL afterDelay:0.2];
}
-(void) setCorrectFocus {
[self.mySearchBar becomeFirstResponder];
}
Transitions between controllers are done manually in code like this:
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"myStoryboard" bundle:nil];
AController *a = [storyboard instantiateViewControllerWithIdentifier:#"A"];
[self.navigationController pushViewController:a animated:NO];
Try this
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self setCorrectFocus];
}
-(void)setCorrectFocus
{
[self.mySearchBar becomeFirstResponder];
}
After long searching I found Error. Somewhere in storyboard old connection for UISearchBar with the name mySearchbar was left. Another thing that I had to correct was that the method
- (BOOL)searchBarShouldEndEditing:(UISearchBar *)searchBar;
returned NO, every time I went back to previous Controller. But it should return YES to be able to set focus on search bar next time.
Was strugling with the same - had a UISearchBar inside a UITextView's keyboard accessory and even after [self.searchBar becomeFirstResponder] was called, keyboard input was still received by the text view.
This is what finally helped:
[self.searchBar.window makeKeyAndVisible];
Apparently the keyboard accessory is part of a different UIWindow than the UITextField.
Note: To continue typing in the text view, you should then call something like [self.textView.window makeKeyAndVisible]; ...
Have you tried adding self.mySearchBar.delegate = self;
You should use strong in your property
#property (strong, nonatomic) IBOutlet UISearchBar *mySearchBar;
Always set strong in your property for IBOutlet. When you pop B from navigationController, mySearchBar has released, so you push back to B, [self.mySearchBar becomeFirstResponder] is not work.
When I pop a UIViewController instance off of my UINavigationController, I find that its properties remain (NSTimers keep timing, AVAudioPlayers keep playing, etc.). I'm wondering what's wrong with my approach?
I push the UIViewController instance onto the UINavigationController this way:
- (IBAction)buttonPressed:(id)sender {
UINavigationController *nc=[self navigationController];
NewViewController *nvc=[[NewViewController alloc] init];
[nvc setNameToUse:[self nameToUse]];
[nc pushViewController:nvc2 animated:YES];
}
The NewViewController has a sub-viewcontroller, that is added via NiewViewController's viewDidLoad method:
self.mySubViewController=[[SubViewViewController alloc] initWithName:self.nameToUse];
self.mySubViewController.view.frame=CGRectMake(0,
0,
self.mySubViewController.view.frame.size.width,
self.mySubViewController.view.frame.size.height);
[self.view addSubview:self.mySubViewController.view];
It's the properties of SubViewController that don't go away when NewViewController is popped. One of these in particular is a timer, declared as follows:
#property (nonatomic) NSTimer *aTimer;
Any advice on this would be terrific. I'm hoping that by solving this issue, the crashes that have been happening once in a while (after the app has been running for 45 or so minutes) will stop! Or at least I'll have a better idea of what's causing them... :) Thanks for reading.
NSTimer retains it's target, if you are passing self (your SubViewViewController) then you'll be creating a retain cycle between the view controller and the timer.
Simply adding SubViewController's view to ViewController's will not correctly pass events.
You need to correctly implement UIViewController child containment, as such:
So your second code block should be something like:
[self addChildViewController:self.mySubViewController];
[self.view addSubview:self.mySubViewController.view];
[self.mySubViewController didMoveToParentViewController:self];
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;
}
}