How do I present a UIViewController modally when a button on it's parent view controller is tapped? - objective-c

I'm having a little issue with an app I've completed. The problem is with a search view controller presented modally.
I have a search button that when tapped presents the SearchViewController modally.
I have 3 different controllers it can be presented from.
MainViewController > CollectionViewController > DetailViewController
This is actually how the controllers are in the hierarchy.
The results are always displayed on the collection view controller. Basically the collection view is refreshed and the remaining cells show are the result of the search.
Searching from collection view controller:
In the collection view controller, when the search is tapped. The search view controller is presented modally. Search text is entered and a list of matches is shown. When a row is tapped then the search view controller is dismissed and notifies the collection view controller using delegation the collection view is them refreshed with the results.
Searching from detail view controller:
In the detail view controller, when the search is tapped. The detail view controller is popped off the stack revealing the collection view controller. I use delegation to notify the collection view controller that the detail view controller was popped of the stack after the tap of a search button. Immediately the search view controller is opened making it possible to search as if we originally presented the search view controller from the collection view controller.
My issue arises when trying to search from my main view controller. Right now things are working but it doesn't have a very professional feel to it. Let me explain.
To get search working from my main view controller I use delegation. So in the method connected to the search button I perform this segue:
- (void)searchButtonTapped
{
[self performSegueWithIdentifier:#"garmentsCollectionSegue" sender:nil];
}
The protocol is defined in my interface file:
#class VAGMainTableViewViewController;
#protocol VAGMainTableViewControllerDelegate <NSObject>
- (void)mainTableViewControllerDisappearedwithTitleForObject:(NSString *)titleString;
- (void)searchButtonOnMainTableViewControllerTapped;
#end
#interface VAGMainTableViewController : UITableViewController
#property (nonatomic, weak) id<VAGMainTableViewControllerDelegate> aDelegate;
In my preparation before segue I have this:
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
[self setADelegate: [segue destinationViewController]];
[[self aDelegate] searchButtonOnMainTableViewControllerTapped];
}
Here I set the delegate and also make a call to the delegate method, making the collection view controller aware of the button tap in my main view controller.
Finally in my collection view controller when the tap is detected a call to the method that presents the search view controller modally is made. Search view controller is presented.
In collection view controller
- (void)searchButtonOnMainTableViewControllerTapped
{
[self searchButtonTapped];
}
- (void)searchButtonTapped
{
VAGSearchViewController *svc = [[self storyboard] instantiateViewControllerWithIdentifier:#"searchPageSB"];
svc.delegate = self;
[self presentViewController:svc animated:NO completion:nil];
}
The search view controller notifies the collection view controller it was dismissed using delegation. This is when the querying and refresh of the collection view controller is done and the result of the search is shown.
- (void)searchViewControllerDismissed:(VAGSearchViewController*)searchViewController withTitleForObject:(NSString *)titleString
{
_searchTitleString = titleString;
[self setObjects:nil];
[self performQuery];
}
Ok so this works but it doesn't look professional because for a split second after the push to the collection view controller from the main view controller the collection view is shown before the search view controller is presented.
The feel I'm aiming for is a snappy one. So I'd prefer if the search view controller would be presented instantly.
Hopefully my detailed post give you an idea of what I'm doing.
There is a much more efficient way to do this. The problem is I can't figure it out.
Would appreciate some help
Thanks for your patience

You can do one thing did not push Collection view controller when search button is tapped.
After pushing modalviewControler if search done then you Call the delegate in MainviewController and Then push collection Viewcontroller with no animation in DelegateMethod. its look what you want

Related

How do I use SWRevealViewController with unwind segues?

I have a sequence of views that are pushed on top of each other on a Navigation Controller. I would like to do two things with these views:
To open the Rear menu view from all of them;
To be able to navigate
back the stack using Unwind segues.
I found out that if I push view controllers on top of each other using regular push segues then the unwind segue works as expected, but then the self.revealViewController on each view controller is not set and the Menu can't be called using the revealToggle: selector.
If I change the push segues to subclasses of SWRevealViewControllerSeguePushController then the views are pushed on top of each other and the Menu can be called from any of them using the revealToggle. Unfortunately, the unwind segues stop working (I think this might be because the view controllers are stacked using addChild instead of pushViewController on the SWRevealViewController class).
Is there a way of working together with SWRevealViewController and Unwind Segues?
Below there is an example storyboard:
The first view controller is the navigation controller; the second is the SWRevealViewController; the three view controllers below navigate one to each other, and the third has an unwind segue to the first. The first and third controllers have buttons that open the menu.
As I stated before, if the segues between the bottom view controllers are regular push segues then the unwind segue works as expected; the menu button from the first view controller works (as it is connected directly to the SWRevealViewController), but the menu button from the third view controller doesn't.
Switching the segue types to SWRevealViewControllerSeguePushController makes the menu buttons from the first and third view controllers work correctly, but the unwind segue stops working.
Oh, and I tested using "popToRootViewControllerAnimated:" and it also doesn't work if the segues are set to SWRevealViewControllerSeguePushController.
I posted this question on SWRevealViewControllers github site and received an answer from Patrick Bodet who was EXTREMELY helpful. I will post the answer below so it may help somebody in the same situation as I.
I had to update the storyboard and added an additional navigation controller as shown below.
As shown in the figure, I wanted to be able to push view controllers on top of each other and also to unwind the segues both to the Login screen (from the Menu) and from stacked view controllers.
On my previous attempts, it seemed as if SWRevealViewController wasn't able to cope with proper navigation segues. Patrick's first suggestion was to move the original navigation controller from before the RevealViewController to before the First view controller. That actually worked, by I still needed to be able to unwind segue from the Menu to the Login screen, so I needed an additional navigation controller.
As suggested by Patrick, I added an additional Navigation Controller. And embarrassingly at the end I realised the button that pointed from the third to the first view controller had both an ibaction and a segue to the first, so that was why it was acting all weird! :-(
So, for the storyboard shown above, in order to work you just use regular Push segues for the view controllers. No need to use SWRevealViewControllerSeguePushController segues.
The code for First and Third view controllers is like this:
#import "ThirdViewController.h"
#import "SWRevealViewController.h"
#import "FirstViewController.h"
#interface ThirdViewController ()
#property (weak, nonatomic) IBOutlet UIBarButtonItem *menuButton;
#end
#implementation ThirdViewController
- (void)viewDidLoad {
[super viewDidLoad];
SWRevealViewController *revealViewController = self.revealViewController;
if (revealViewController) {
[self.menuButton setTarget: revealViewController];
[self.menuButton setAction: #selector( revealToggle: )];
}
}
- (IBAction)returnToFirst:(id)sender {
[self performSegueWithIdentifier:#"First" sender:self];
//[self.navigationController popToRootViewControllerAnimated:YES];
}
#end

Dismiss UITableViewController on tableView:didSelectRowAtIndexPath:indexPath

Currently I have a UIView with a UItableview cell added to the subview, like so,
When the button is pressed, it pushes to another navigation controller whos subclass is a uitableviewcontroller....
So When the user clicks on one of these tableviewcells... I want the viewcontroller to be poped from the navigation stack and go back to the previous view that is listed first.
I implemented the UITableviewcontroller method listed below with the following implementation.... but nothing occurs :P....
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self dismissViewControllerAnimated:YES completion:nil];
}
Any ideas?
If you are pushing the view controller onto the navigation controller's stack i.e. pushViewController:animated:, you should pop out current view controller using popViewControllerAnimated: method.
If you want to go to a view controller not just one level below the current view controller (or top view controller in UINavigationController's jargon), you can use popToViewController:animated:. And to go to the root view controller of the navigation controller, use popToRootViewControllerAnimated:.
But if you are presenting a view controller modally i.e. presentViewController:animated:completion:, then only you use the dismissViewControllerAnimated:completion: method to dismiss the presented view and go to the presenting view controller.

Dismissing the split view popover controller

I have a UISplitViewController with the master view set up like this:
UITabBarController
Tab1:
UINavigationController -> UIViewController -> UIViewController
Tab2:
UINavigationController -> UIViewController
Each of the UIViewControllers is a table view, and when the user chooses a row in the last one, an image is shown in the detail view, which contains a UIScrollView.
The tab bar Controller is the UISplitViewControllerDelegate and handles putting up the button on a toolbar at the top of the scroll view.
The problem is, I want to add code to dismiss the popover when the user makes their choice. The pointer to the popover has to be saved in the tab bar controller when the button goes up, and then used to dismiss the popover several view controllers down the line when the user makes their final selection. There doesn't seem to be any way for the view controller that needs that pointer to get at it, without doing something gross like storing it in the App Delegate.
I don't see other people asking this question, which leads me to believe that I've once again overlooked something simple. Please enlighten me!
It sounds like your tab bar controller is already a subclass of UITabBarController, which means that you've already got some custom code in there. I would suggest that the tab bar controller is the primary owner of the popover, and it is the table view controller's responsibility to simply notify the tab bar controller that a selection has been made. The tab bar controller can respond to that message by dismissing the popover. You can take advantage of the fact that UIViewController already has a method for accessing the tab bar controller that contains a given controller.
So it would look something like this:
#interface MyTabBarController : UITabBarController
- (void)itemWasSelected;
#end
#implementation MyTabBarController {
UIPopoverController *popover;
}
- (void)itemWasSelected {
[popover dismissPopoverControllerAnimated:YES];
}
#end
//////////////
#implementation TableController
- (void)tableView:(UITableView *)tv didSelectRowAtIndexPath:(NSIndexPath *)path {
// Do whatever else you want to do
MyTabBarController *tabController = (MyTabBarController *)self.tabBarController;
[tabController itemWasSelected];
}
With this solution, the table controller doesn't have to know anything about the popover; it just has to know that it's going to be presented inside a MyTabBarController, which seems a reasonable thing for it to know.
You could create a singleton class to track your popover status and then make it available to all classes equally and easily. That way it could easily be updated and accessed from any code without having to go straight to overburdening the app delegate even though thats basically the same idea but a bit cleaner in its own singleton.

Reference the main view controller... need the code

I have a view based app (not navigation or tab based...)
My main view controller is called from the app delegate and initiated from a xib.
Then I use presentModalViewController to bring another view on the screen with it's own xib and view controller.
I have no problems passing data to that view controller.
However, when I dismiss the second view controller, I want to send data back to the main view controller for my app, but I just can't figure out how to reference it. Actually, I'd like to call a method in the main view controller if possible.
I've been struggling with this a bit and have found suggestions online but I just can't seem to get it to work. I'm hoping someone can provide the sample code to do this.
P.s. is this "main view controller" still referred to as a "root view controller" or is that term only used when dealing with a view controller stack (i.e. navigation or tab view controller)
EDIT:
I'm sure Bryan's solution would work so I have accepted as answer. However I ended up using NSNotificationCenter to get this to work and I find it a bit simpler to understand as a beginner
You can use the delegation pattern. In your modal view controller's header file, create an interface for a new delegate protocol...
#protocol ModalViewControllerDelegate <NSObject>
- (void)sendData:(Data *)someData;
#end
...and give your ModalViewController a new instance variable that implements this protocol:
#property (nonatomic, assign) id<ModalViewControllerDelegate> delegate;
Your main view controller should implement this protocol...
#interface MainViewController : UIViewController <ModalViewControllerDelegate> {
...and set itself as the delegate before it presents the modal view controller:
ModalViewController *modalViewController = [[[ModalViewController alloc] init] autorelease];
[modalViewController setDelegate:self];
// Present modal view controller
The main view controller should implement the delegate protocol's method:
- (void)sendData:(Data *)someData {
NSLog("I have just received some data: %#", someData);
}
Then inside your modal view controller, you can simply call the following method whenever you want to send data back to the main view controller:
[delegate sendData:someData];

UISplitViewController not calling delegate methods while pushing new detailView

I setup a storyboard based on the Master-Detail Application, embed the detail view in a navigation controller, and add a new table view controller object which I will use as a second detail view controller.
I then push the new detail view controller with the following code (instead of a segue because I am pushing both a root view and a detail view controller at the same time. Only the detail view code is shown).
// Push the detailView view controller:
NewClass *newViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"test"];
newViewController.navigationItem.hidesBackButton = YES;
self.splitViewController.delegate = newViewController;
[self.detailViewController pushViewController:newViewController animated:YES];
This works perfectly, EXCEPT that the splitView delegate methods are never called before or after the push. If I do this while in portrait mode, after it pushes the detailViewController, the button to drop down the masterView popover does not show up UNTIL I rotate to landscape mode and then back to portrait mode.
How can I cause the willHideViewController/willShowViewController split view controller delegate methods to be called or manually cause them to be called?
So from what I found, it doesn't call the method because the orientation hasn't changed.
What you have to do is to pass the button from the presenting view controller since it's already tied to the popover like this:
if(self.navigationItem.leftBarButtonItem != nil) {
newViewController.navigationItem.leftBarButtonItem = self.navigationItem.leftBarButtonItem;
}
// Push the newViewController