I have a main viewController xib and depending on the buttons the user clicks it could take them to one of 4 viewControllers xib I have seen this code and seem to be having a error.
iOS: Returning to main XIB from secondary XIB
- (IBAction)Button100:(id)sender
{
[self.aView removeFromSuperview];
self.aView = nil;
}
It canot find the aView in the second xib should the #property (strong) UIView *aView; go in the main appdeligte or just in the main .h file of the first xib?
So I understand that You just want to show and hide appropriate views depending on the button clicked.
One way to do it is to put this in Your root view controller:
-(IBAction) buttonXClicked {
[self presentModalViewController:someViewController animated:YES];
}
(I assume here that You already know how to link a button with it's action, if not please read this tutorial: http://rshankar.com/different-ways-to-connect-ibaction-to-uibutton/ )
and then in Your child view:
-(IBAction) backButtonClicked {
[self dismissModalViewControllerAnimated:YES];
}
Another way (though without animations) is in Your root view controller:
-(IBAction) buttonXClicked {
[self.view addSubview:someViewController.view];
}
and in the child view:
-(IBAction) buttonXClicked {
[self.view removeFromSuperview];
}
I also recommend You check this tutorial on using a navigation controller (which should look better):
http://iosmadesimple.blogspot.com/2012/09/navigation-based-project-doing-it-using.html
Related
So I am trying to add a view as a subview onto an exisiting View Controller by loading it from an XIB in such manner -
- (void)showInView:(UIView *)aView animated:(BOOL)animated
{
self.view.frame = aView.frame;
dispatch_async(dispatch_get_main_queue(), ^{
[aView addSubview:self.view];
if (animated) {
[self showAnimation];
}
});
}
I see the button on the parent View Controller and it gets pressed too with the little animation that is there on UIButtons by default. The problem is that the IBAction connected to the UIButton never gets called.
Thanks in advance.
Adding the view as a class variable instead of a local variable did it for me.
I have a ViewController (with a container view) embedded in a Navigation controller. The container contains a pageViewController with one of the 'pages' being a TableViewController (with UITableView outlet: 'aTableView'). I want to trigger the edit mode in the tableViewController when tapping a custom editButton in the navigation bar. When I create a custom editutton in the tableViewController the edit mode works as expected, but when I use the custom editButton in the navigation bar the setEditing bool value remains zero even when I setEditing to YES in the editButton selector. Here's the code:
ViewController.m
-(void)viewDidLoad {
self.editBtn = [UIButton buttonWithType:UIButtonTypeCustom];
[self.editBtn setFrame:CGRectMake(0, 0, 40, 22)];
[self.editBtn addTarget:self action:#selector(goToToggleEdit:) forControlEvents:UIControlEventTouchUpInside];
UIBarButtonItem *editButton=[[UIBarButtonItem alloc]initWithCustomView:self.editBtn];
self.navigationItem.rightBarButtonItem = editButton;
}
-(void)goToToggleEdit:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
TableViewController *tvc = [storyboard instantiateViewControllerWithIdentifier:#"aTableViewController"];
if(something==foo){
[tvc toggleEdit];
}
}
aTableViewController.h
#interface aTableViewController : UITableViewController <UITextFieldDelegate> {
IBOutlet UITableView *aTableView;
}
-(void) toggleEdit ;
#end
aTableViewController.m
-(void)toggleEdit {
[aTableView setEditing:YES animated:YES];
NSLog(aTableView.editing ? #"Yes" : #"No"); // --> logss 'No'.
if (aTableView.editing){
//do something
}
else {
//do something else
}
}
How can I efficiently trigger the edit mode in the tableViewController this way?
Edit
#Bagrat Kirakosian pointed out to me that my view hierarchy (Navigation Controller > View Controller (with containter) > Page View Controller (in container) > Table View Controller) might be the problem. I just want to create a Navigation Bar (with an edit button) that is fixed, therefore I can't embed the Table View Controller directly in a Navigation Controller.
Thanks.
UPDATE: Solution
I have accepted #sebastien's solution although both #sebastien's and #Bagrat's solution work great. #Bagrat's answer includes direct access to the Table View Controllers, while #sebastien's solution calls edit mode in the pageViewController. I think, considering the tricky hierarchy, the latter is a bit more secure.
Here is the code for my View Controller that totally work fine. be sure you configure your bar button in the right method of View Controller lifecycle. Also be sure that your #selector is properly implemented in your code.
In the same View Controller put these two blocks of code
-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
UIBarButtonItem *rightBarButton = [[UIBarButtonItem alloc] initWithImage:[[UIImage imageNamed:#"edit_icon.png" ] imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate] style:UIBarButtonItemStylePlain target:self action:#selector(edit:)];
[rightBarButton setTintColor:[UIColor whiteColor]];
self.navigationItem.rightBarButtonItem = rightBarButton;
}
Afterwards you need also to put your edit selector
-(void)edit:(UIButton *)sender {
// Toggle edit by inverting current edit state
// Also in this block change your right bar button text or image
[self.tableView setEditing:!self.tableView.editing animated:YES];
}
UPDATE 1
After your comment we got to whole another question. You problem is not in the part where you try to call toggle edit. Your problem is the wrong hierarchy of controllers (Navigation Controller > View Controller > Page View Controller > Table View Controller). This might cause a problem. Try to change your controllers like this;
UINavigationController > UIPageViewController > UIViewController(s)
Also it's a good practice to have a UITableView in UIViewController rather than using really dead UITableViewController. Don't forget to connect your tableView IBOutlet (by the way you didn't need it in UITableViewController), also connect datasource and delegate to Files owner. In your MyTableViewVC.h file add this line
#interface MyTableViewVC : UIViewController <UITableViewDataSource, UITableViewDelegate>
After that all your calls will work fine.
UPDATE 2
After analyzing your entire structure I found a mistake that you do every time on the button click.
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
tvc = [[TodolistTableViewController alloc] init];
When you call storyboard every time it's ok but when you do [[TodolistTableViewController alloc] init] you are RE-MAKING the same table view controller every time but not even adding it to your main view. As I told you, your tricky hierarchy might cause difficulties but it has a solution.
In your PageViewController.m make tv1 and tv2 properties in .h file, like so.
#property (strong, nonatomic) UITableViewController *tv1;
#property (strong, nonatomic) UITableViewController *tv2;
Then in the view controller file do this
-(void)toggleEdit:(id)sender
{
PageViewController *current = (PageViewController *)[[self childViewControllers] firstObject];
if ([current isKindOfClass:[PageViewController class]])
{
[((TodolistTableViewController *)[current tv1]) toggleEdit];
}
}
Answer includes all security checks and direct access to your table view controllers, because you might need to change other properties/call functions later.
Now in -(void)toggleEdit:(id)sender you don't re-create your views every time but you catch the ones you already have in your current View Controller.
Good Luck!
Ok, your issue here is that you are trying to access an embedded controller in a wrong way.
You are actually managing 2 differents PageViewController:
The one you generated through your storyboard
The other one you are initiating in your code further
That's why you can't reach the expected result.
First of all, add a new method to your PageViewController:
PageViewController.h:
- (void)editTableAtIndex:(int)index;
PageViewController.m:
- (void)editTableAtIndex:(int)index {
[[self viewControllerAtIndex:index] setEditing:YES];
}
Now, in your main ViewController, access the PageViewController by using childViewControllers:
-(void)toggleEdit:(id)sender
{
PageViewController *pvc = self.childViewControllers[0];
[pvc editTableAtIndex:0];
}
It should be editing your TodoListTableView:
(Please notice that I used [pvc editTableAtIndex:0];, instead you should be calling something like [pvc editTableAtIndex:_PageViewController_current_index_];)
I'm trying to make a form that spans three tabs. You can see in the screenshot below where the tabs will be. When the user taps a tab, the Container View should update and show a particular view controller I have.
Tab 1 = View Controller 1
Tab 2 = View Controller 2
Tab 3 = View Controller 3
The view controller shown above has the class PPAddEntryViewController.m. I created an outlet for the Container view within this class and now have a Container View property:
#property (weak, nonatomic) IBOutlet UIView *container;
I also have my IBActions for my tabs ready:
- (IBAction)tab1:(id)sender {
//...
}
- (IBAction)tab2:(id)sender {
//...
}
- (IBAction)tab3:(id)sender {
//...
}
How do I set the container in those IBActions to change the view controller that the Container View holds?
Among a few other things, here's what I've tried:
UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"vc1"];
_container.view = viewController1;
...but it doesn't work. Thanks in advance.
Switching using Storyboard, Auto-layout or not, a Button of some sort, and a series of Child View Controllers
You want to add the container view to your view and when the buttons that 'switch' child view controllers are pressed fire off the appropriate segue and perform the correct setup work.
In the Storyboard you can only connect one Embed Segue to the Container View. So you create an intermediate handling controller. Make the embed segue and give it an identifier, for example EmbededSegueIdentifier.
In your parent view controller wire up the button or whatever you want and keep are reference to your child view controller in the prepare segue. As soon as the parent view controller loads the segue will be fired.
The Parent View Controller
#property (weak, nonatomic) MyContainerViewController *myContainerViewController;
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"EmbeddedSegueIdentifier"]) {
self.myContainerViewController = segue.destinationViewController;
}
}
It should be fairly easy for you to delegate to your container controller the button presses.
The Container Controller
This next bit of code was partly borrowed from a couple of sources, but the key change is that auto layout is being used as opposed to explicit frames. There is nothing preventing you from simply changing out the lines [self addConstraintsForViewController:] for viewController.view.frame = self.view.bounds. In the Storyboard this Container View Controller doesn't do anything more that segue to the destination child view controllers.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%s", __PRETTY_FUNCTION__);
[self performSegueWithIdentifier:#"FirstViewControllerSegue" sender:nil];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
if ([self.childViewControllers count] > 0) {
UIViewController *fromViewController = [self.childViewControllers firstObject];
[self swapFromViewController:fromViewController toViewController:destinationViewController];
} else {
[self initializeChildViewController:destinationViewController];
}
}
- (void)initializeChildViewController:(UIViewController *)viewController
{
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[self addConstraintsForViewController:viewController];
[viewController didMoveToParentViewController:self];
}
- (void)swapFromViewController:(UIViewController *)fromViewController toViewController:(UIViewController *)toViewController
{
[fromViewController willMoveToParentViewController:nil];
[self addChildViewController:toViewController];
[self transitionFromViewController:fromViewController toViewController:toViewController duration:0.2f options:UIViewAnimationOptionTransitionCrossDissolve animations:nil completion:^(BOOL finished) {
[self addConstraintsForViewController:toViewController];
[fromViewController removeFromParentViewController];
[toViewController didMoveToParentViewController:self];
}];
}
- (void)addConstraintsForViewController:(UIViewController *)viewController
{
UIView *containerView = self.view;
UIView *childView = viewController.view;
[childView setTranslatesAutoresizingMaskIntoConstraints:NO];
[containerView addSubview:childView];
NSDictionary *views = NSDictionaryOfVariableBindings(childView);
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"V:|[childView]|"
options:0
metrics:nil
views:views]];
[containerView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:#"H:|[childView]|"
options:0
metrics:nil
views:views]];
}
#pragma mark - Setters
- (void)setSelectedControl:(ViewControllerSelectionType)selectedControl
{
_selectedControl = selectedControl;
switch (self.selectedControl) {
case kFirstViewController:
[self performSegueWithIdentifier:#"FirstViewControllerSegue" sender:nil];
break;
case kSecondViewController:
[self performSegueWithIdentifier:#"SecondViewControllerSegue" sender:nil];
break;
default:
break;
}
}
The Custom Segues
The last thing you need is a custom segue that does nothing, going to each destination with the appropriate segue identifier that is called from the Container View Controller. If you don't put in an empty perform method the app will crash. Normally you could do some custom transition animation here.
#implementation SHCDummySegue
#interface SHCDummySegue : UIStoryboardSegue
#end
- (void)perform
{
// This space intentionally left blank
}
#end
I recently found the perfect sample code for what I was trying to do. It includes the Storyboard implementation and all the relevant segues and code. It was really helpful.
https://github.com/mhaddl/MHCustomTabBarController
Update: UITabBarController is the recommended way to go, as you found out earlier. In case you'd like to have a custom height, here is a good start: My way of customizing UITabBarController's tabbar - Stackoverflow answer
As of iOS 5+ you have access to customize the appearance via this API; UIAppearance Protocol Reference. Here is a nice tutorial for that: How To Customize Tab Bar Background and Appearance
The most obvious way to achieve what you're looking for is to simply manage 3 different containers (they are simple UIViews) and implement each of them to hold whatever content view you need for each tab (use the hidden property of the containers).
Here is an example of what's possible to achieve with different containers:
These containers "swapping" can be animated of course. About your self-answer, you probably chose the right way to do it.
have a member variable to hold the viewController:
UIViewController *selectedViewController;
now in the IBActions, switch that AND the view. e.g.
- (IBAction)tab1:(id)sender {
UIViewController *viewController1 = [self.storyboard instantiateViewControllerWithIdentifier:#"vc1"];
_container.view = viewController1.view;
selectedViewController = viewController1;
}
to fire view did appear and stuff call removeChildViewController, didMoveToParent, addChildViewController, didMoveToParent
I got this to work by using a UITabBarController. In order to use custom tabs, I had to subclass the TabBarController and add the buttons to the controller in code. I then listen for tap events on the buttons and set the selectedIndex for each tab.
It was pretty straight forward, but it's a lot of junk in my Storyboard for something as simple as 3 tabs.
Let me explain. I have multiple UIViewControllers. On my MainPageController, I have 3 UIViews. Let's enumerate it this way: the first UIView is called LoginView, the second is called HomeView and the other one is called RegView. Now in HomeView, there are multiple buttons that will lead to other UIViewControllers. For example, one button will lead to StoreController. Now if I am inside StoreController and I want to go back to MainPageController, I simply call:
[self dismissModalViewControllerAnimated:YES completion:nil]
This will send me back to the HomeView.
That is good. However, inside the StoreController, there are buttons which will supposedly direct me to LoginView or RegView, whichever button was tapped. The problem is when the method [self dismissModalViewControllerAnimated:YES completion:nil], it only take me back to HomeView, no matter which button I pressed.
So how will I display the right UIView once the dismissModalViewControllerAnimated is called?
EDIT:
This is how I show the UIViews:
-(void)viewDidLoad
{
//Initialize the views here...
}
-(void)showViewByTag:(NSInteger)tag
{
if (tag == 1)
{
[self.view addSubview:loginView];
}
else if (tag == 2)
{
[self.view addSubview:homeView];
}
else
{
[self.view addSubview:regView];
}
}
Now I call the method showViewByTag: somewhere in my code to display the views.
What you could try and do is following: before calling [self dismissModalViewControllerAnimated:YES completion:nil] (and thus go back to your home view), change the view currently displayed in your MainPageController:
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
If you are worried at the cast and you foresee that self.presentingViewController might be not of MainPageController type on some occasions, then you can check explicitly for its type:
if ([self.presentingViewController isKindOf:[MainPageController class]])
[(MainPageController*)self.presentingViewController showViewByTag:desiredViewTag];
[self dismissModalViewControllerAnimated:YES...];
For this to compile, MainPageController.h must be imported in your modal controller class.
dismissModalViewController will always bring back the viewController which presented it ,and that can be only one,so the ideal way would be to tell the navigationController to initWith your desired viewController..
eg on regButton click in the presented modalview
RegViewController *regViewController = [[RegViewController alloc]initWithNibNam:#"RegViewController" bundle:nil];
[self.navigationController initWithRootViewController:regViewController];
Hy
i have two classes uiviewcontroller and uiview. I have one view controller. Inside i have uiview. Inside uiview i have textfield and when i write a text and click done i need to refresh uiviewcontroller.
I tried with this in uiview class:
-(IBAction)textFieldReturn:(id)sender
{
ViewController *vc = [[ViewController alloc] init];
[vc viewDidLoad];
}
i need refresh the same as you click the button and open viewcontroller.
I am guessing you mean that you want to "refresh" the view, not the view controller. To do that simply call [self setNeedsDisplay] from the view, or [self.view setNeedsDisplay] from the view controller. Also make sure that the textfield is a subview of the uiview. Either do that in the nib file or in code by calling [self addSubview: (textfield here)].
Also, if you want to access the view controller from the view you will need to create an IBOutlet, simply allocating a new ViewController object within the view does not mean that the created view controller controls the view. Hopefully that makes sense. I'd recommend going through some ios starter tutorials as well. Just google that there are a lot.