I'm little lost here. I have a basic master-detail aplication, and I want to change the detail view according to the selected row in MasterViewController, but the views have different content, one has an image gallery, and the other one will load a video on full screen. It's not just refresh the detail view, have to load another view. How is the better(fast) way to do that?
I'll suggest you to use a replace segue.
Just create a segue to the desired view initiated by your row with a Style: Replace, and Destination: Detail Split.
Segues were introduced in the iOS 5 SDK
EDIT:
These are step-by-step instructions to accomplish what you need.
From now on:
the item that should be pressed for the action to take place (a button or row in master view) = *Button;
the view you want to be placed in the iPad's detail view = *Detail.
just a bit for naming for ease of explanation
Hold ctrl click on *Button then hold and drag to *Detail and release to create your segue.
In the popup pick Replace
Select your segue, open Attributes inspector and set Destination to Detail Split
That's all.
More on segues: http://www.scott-sherwood.com/?p=219
Specifically, in CS193P, check out the latest version, and look at lecture #7. Paul doesnt finish the part with the replace Segue, but he DOES provide an example with very good reusable code (Psychologist with Dr Pill)
If you are using a dynamic tableview in your MasterViewController, implement numberOfRowsInSection:(NSInteger)section Method with:
return [_youDataArrayNameHere count];
then on cellForRowAtIndexPath configure the cell:
cell.textLabel.text = [_youDataArrayNameHere objectAtIndex:indexPath.row];
and in didSelectRowAtIndexPath, call any other view based on the selected row:
//
if (indexPath.row == 0) {
[_detailViewController.navigationController pushViewController:[self.storyboard instantiateViewControllerWithIdentifier:#"anotherVC01Here"] animated:YES];
}
Related
I have a custom class Medication with variables name and directions. I have this in an array presenting itself in a tableview showing Medication.name and Medication.directions.
I'd like the user to be able to click either on the row or on the accessory button (Detail?) to edit the value in the selected row.
My ideal situation would be a menu that dropped down from the bottom of the selected row with editable fields that would update the row in the array and reload the tableview.
Is there a way to accomplish this or something similar? I was playing around with hidden view fields and having them show when the row is selected, but I'm not sure if I can anchor one to the bottom of the selected row? Or I could build a custom cell that has the editable fields in each cell, hidden, with a fixed height to the cell that changes upon clicking it to show the extra fields.
Likewise, if the best option is to use the accessory view, is Detail the most appropriate one? I can't seem to right click and drag a segue for this. Or would I just use this below?
optional func tableView(_ tableView: UITableView,
accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath)
If I segue this to another view controller, I'll need to use delegates obviously to change the array from the tableview so I'm hoping to find a simpler solution.
Thanks! Appreciate any tips for this newbie
Here's where I'm stuck with the segue:
let vc = segue.destinationViewController as! MedDetailViewController
vc.delegate = self
let MedRow = self.tableView.indexPathForSelectedRow()!
let passMed = myMedsList[MedRow] //this row doesn't work
vc.detailMed = passMed
A segue is not only the simplest way to accomplish this, you can also take advantage of an unwind segue to automatically return your edited values.
No delegation needed. No custom code to add hidden views to your cell.
If you only have one segue, add it to the row, not the accessory. Users tend to expect that tapping on the row does something. If it didn't do anything, most users wouldn't then try tapping on the accessory.
Tapping on the accessory is meant as a secondary action that does something different from tapping on the row.
I would setup the show segue right in Storyboard. Less code to write, less code to maintain. Let Storyboard do the work for you.
Update:
Here's Apple's tech note about Using Unwind Segues, along with their Swift example of how the destination (presenting) view controller accesses data from the source (presented) view controller.
#IBAction func unwindToMainMenu(sender: UIStoryboardSegue)
{
let sourceViewController = sender.sourceViewController as! MedDetailViewController
// Update the medsList array with the new detailMed
let medRow = self.tableView.indexPathForSelectedRow().row
medsList[medRow] = sourceViewController.detailMed
}
Update 2:
You want to update the detail view controller's detailMed property before you unwind the segue, using the prepareForSegue: method.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if segue.identifier == "unwindToMedications"
{
// Update the detailMed if the user has edited it
self.detailMed = ... // the edited medication you're returning
}
}
Update 3:
The back button (or swiping back) simply pops the pushed view controller off the stack, but there's no segue that's being called. You need to explicitly hook up a segue from a Done button that will unwind for you and update the meds list.
The reason why you explicitly unwind is to distinguish between whether the user wants to commit or cancel any change. (Did they make a change, but they want to ignore it, so they pressed Back?)
You have two options, which both will work, and it's up to you how you want to present the editable details. (I prefer push to show details, and modal for editing forms, but either way works).
Push the detail view controller. Have a Done button. The user can use the Back button to cancel, or the Done button to accept any changed med details.
Present a modal view controller with Cancel and Done buttons. Again, the user can cancel (return without changes), or tap Done to (return and) accept changes.
For either presentation, you hook up an unwind segue to the Done button. What this will do is call the Meds list view controller unwind action. In that unwind code, you get the med details and update the meds list, as we previously discussed.
A mention about Core Data:
There's an alternative approach which isn't simpler to learn than segues, but it's a great fit. (Something to keep in mind, if not for this app, then for the next one.)
Core Data and NSFetchedResultsController. You save the edited medication, and NSFetchedResultsControllerDelegate updates the list of medications for you.
Some might say it's overkill, but since I started using NSFetchedResultsController, I wish I had learned it sooner.
I am trying to create push segue from view. Maybe image would be best for describing:
I started from sample ECSlidingViewController project (BasicMenu) and I am trying to expand first ViewController (Home) to another ViewController. I get it and I can go from selected row in tableView to the controller. But when I am in controller and I tap on Back I am at different screen from first one (it's blank screen with button at upper left). I guess I must set something more to get this working but I don't know what. Thanks
Updated:
Code from first view controller to go to next view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Find the selected cell in the usual way
UITableViewCell *cell = [self.searchResultsTableView cellForRowAtIndexPath:indexPath];
[self performSegueWithIdentifier:#"selectedSegue" sender:cell];
}
I found that the problem is with code in method didSelectRowAtIndexPath. When I removed it. The segue is performed well and I go to other view controller and when I tap on top left button I get back where I was. It's okay.
So I guess the real problem was with sender in performSegueWithIdentifier.
But I need send to new controller some information about selected row. So I used this answer.
I'm brand new when it comes to app development so this might be a stupid question.
So i have made a UI table. It is customizable, as in users can insert or delete rows. I want to allow users to click on a table cell and it'll direct them to another view controller. All the view controllers will look the same for each cell (sorta like a template). Any idea how to implement this using storyboard?
Appreciate it!
You do not need an array of view controllers. All you need is one view controller, which gets instantiated when the user clicks the cell to navigate to it, and gets deallocated as soon as the user closes the screen to go back to your main view controller.
All you need to implement this in your storyboard is adding a push segue from a cell or a button in your main view controller to your "detail" view controller. When the segue gets triggerred, your code gets a chance to configure the newly created "detail" view controller in the prepareForSegue:sender: method, before the controller's view appears on the screen. This is the place where you customize the data that shows up in the detail view (presumably, depending on the particular row in the table that has triggered the segue).
Here is a link to a good tutorial explaining how to build a master-detail application with Xcode and storyboards.
In storyboard you create a viewcontroller that will display the data after a cell has been selected, you will only need one and not an array. Link it from the tableviewcontroller to the new viewcontroller. Click the segue in Xcode and in the inspector give it a unique identifier.
tableView:didSelectRowAtIndexPath: will get called when you select a cell, here you can perform the segue:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
self.selectedObject = ... // store the object that was selected
[self performSegueWithIdentifier:#"mySegue" sender:self];
}
In your tableviewcontroller you make sure you implement prepareForSegue:sender:. Here you can hand over the correct model object to populate your destination viewcontroller with data.
-(void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"mySegue"])
{
MyDetailViewCotroller *controller = [segue destinationViewController];
controller.dataObject = self.selectedObject;
}
}
Check out this example code from Apple (does not used Storyboard though): http://developer.apple.com/library/ios/#samplecode/SimpleDrillDown/Introduction/Intro.html#//apple_ref/doc/uid/DTS40007416
After struggling for days on firing a segue conditionally, I managed to solve it thanks to Simon's answer here. Please take a moment to have a look or you might not understand what I'm talking about below. I didn't copy paste his answer because he's already explained it nicely over there.
Now I've faced a new question. What if I have multiple View Controllers that I want to segue to from one View Controller?
To explain it further : Say I have one MainViewController with 2 buttons. When clicked upon each button, it should segue to their respective View Controller. First button to FirstViewController and the second button to SecondViewController.
The method described in Simon's answer can be used when you segue from one View Controller to another View Controller. Since in that method, you tie the segue to the View Controller itsrlf and not to the button, you have only one segue with an identifier for that particular View Controller. Therefore I cannot distinguish between the button taps separately.
Is there a workaround to solve this problem?
Thank you.
It might be bit premature to say this but I guess you should look into Segue more deeply.
Yes you can perform segure from button. Just control click the button and drag the cursor to view controller you want it SEGUE'd. And from my understanding only condition there is each button tap results a segue to a fixed view. There is no condition there.
Also, you can push the navigation controller manually by
YourViewController *destViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"YourDestinationViewId"];
[self.navigationController pushViewController:destViewController animated:YES];
UPDATE:
prepareForSegue is too late to stop a segue from proceeding. Yes you can create multiple segues from your view to other view controllers. And in this case you have to do so. Don't reate a segue from button, just define a IBACtion on the button click you can do the validation from there,
if(validationSuccess) {
[self performSegueWithIdentifier:#"segue1" sender:self];
}
if you are using ios6
- (BOOL)shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender
return YES on validation success and NO on failure to stop it from proceeding.
I suggest you look a bit at reworking your code logic.
If I understand correctly, you have a VC (embedded in a Nav. Controller) with 2 buttons and you have figured out how to segue each button to a different VC.
Your problem is you want to make sure that even if one of the buttons are pressed, a validation is done before an action takes place. I would advise this is bad User Interface design because the user has the illusion that this button might do something and then they click it and nothing happens.
UIButton can be connected to IBActions (to initiate actions) and IBOutlets (to set their properties). If this is a button created in IB directly, I would connect it to your class as an Outlet property:
#property (nonatomic,weak) IBOutlet UIButton* myButton;
And then set its enabled value:
self.myButton.enabled=NO;
This will keep the button and dim it. This is much better UI design and the user knows they should not press the button because some condition is not satisfied.
I would rework the code so that you set this value as disabled by default for example and enable it appropriately in your code whenever your "condition" is satisfied.
Obviously if this button is created programmatically (in your code without IB) then it is easy to just use the second command above.
Hope this helps.
I just wrote another way to call multiple detail views from a single table. Each cell could essentially make a different view be displayed. The code is similar to what you see in this post but you essentially use identifiers and attributes on the list item to determine which view to show.
https://codebylarry.com/2016/07/15/multiple-detail-views-in-swift/
override func tableView(tableView: UITableView,didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row == 1 {
self.performSegueWithIdentifier("secondView", sender: self)
} else {
self.performSegueWithIdentifier(“others", sender: self)
}
}
I'm having a problem getting a UISearchDisplay's text value to be set programatically on load of the view by another view and my question is have I overcomplicated my problem and missed something or am I on the right track of thinking.
Here's the situation: I have a UITabBarController as my root view, there are 2 tabs, both have a UINavigationController setup so I can push views as needed.
Tab 1 has a UITableViewController which is populated with a list of categories.
Tab 2 has a MapView in it's main view but I have done a custom UINavigationItem view to put various buttons and a UISearchDisplay on the rightBarButtonitem area.
The mapview layout and custom navigation item are stored in the same nib as two separate view objects. In Tab 2's viewDidLoad(), I initialise the rightBarButtonItem programatically with:
UIBarButtonItem *btnItem = [[UIBarButtonItem alloc] initWithCustomView:buttonBar];
self.navigationItem.rightBarButtonItem = btnItem;
[btnItem release];
Everything fires up, buttonBar is wired up to an IBOutlet as searchWhat and I can talk to this object from within the mapview's controller class.
If the user is in Tab 1 and taps a cell, I want it to switch to Tab 2 and populate the searchWhat.text and then execute the search code as if someone had typed in the search themselves.
What i'm having trouble with is the order of load and populate on a view.
I can access the 2nd tab from the 1st without any problem and get it to appear with something like:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"Quick Category cell tapped at row %d", indexPath.row);
self.tabBarController.selectedIndex = 1; // change to the search view controller
//[self.tabBarController.selectedViewController viewDidAppear:YES];
UINavigationController *nav = (UINavigationController *)self.tabBarController.selectedViewController;
SearchViewController *srch = [nav.viewControllers objectAtIndex:0];
//NSLog(#"%#", [srch description]);
[srch queueSearchByType:kSearchTypeQuickCategories withData:[catList objectAtIndex:indexPath.row]];
[srch viewDidAppear:YES];
}
Don't worry about catList and SearchViewController, they exist and this bit works to switch tabs.
Here's the problem though, if the user starts the application and selects an item in tab 1, tab 2 appears but the values of the search display text don't get set - because viewDidLoad and viewDidAppear are called in another thread so the execution of queueSearchByType:withData: gets called while the view is still loading and setting up.
If the user selects tab 2 (therefore initialising the subview) and then selects tab 1 and an item, it can populate the search display text.
I can't just change the order of the tabs so that tab2 is first and therefore loads it's subviews to the navigation bar as the project specification is category search first.
Have I missed something very simple? What I need to do is wait for the second tab to fully appear before calling queueSearchByType:withData: - is there a way to do this?
At the moment, i've implemented a queue the search, check for a queue search approach, this seems to be a bit long winded.
Ok, I don't like answering my own question but it appears my fears were right, basically if you want a UINavigationItem that is a custom view (ie, to put a search bar and various other buttons up on the nav controller) and be able to switch to and populate them from another tab on a tab bar controller, then you need to put the subview in it's own class which is a subclass of UIViewController and then make delegation your friend (which it already is), i've provided an example in case anybody needs to repeat it which i've put on my blog HERE.
http://www.jamesrbrindle.com/developer/ios-developer/howto-add-a-custom-uinavigationitem-to-a-uinavigationcontroller-with-delegation.htm
If anyone disagrees and thinks this can be simpler, please let me know or rate this post