Alright,
I have read some Q&A's on this, but I need to be able to relate it to my code and I am not being successful, I hope someone is able help me:-)
I have a UITableViewController that is populated with a NSMUtableArray.
I would like to link to each entry and then have that URL display in a DetailViewController which has UIWebView in it.
Now I had the idea of passing a string along from one VC to the next, but that does not seem to work.
This is the code I have in the First TVC that is relevant:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
int currentindex = [indexPath row];
switch (currentindex) {
case 0:
_urlAddress = #"http://localhost:8888/thePalmsMenu/breakfast_menu.php";
break;
case 1:
_urlAddress = #"http://localhost:8888/thePalmsMenu/lunch_menu.php";
break;
default:
break;
}
webVC *sView = [[webVC alloc] init];
sView.urlAddress = _urlAddress;
[self.navigationController pushViewController:sView animated:YES];
}
And then I have this in the Second View Controller which has the UIWebView in it.
-(void)viewDidLoad
{
_vc.urlAddress = _urlAddress;
NSURL *url = [NSURL URLWithString:_urlAddress];
NSURLRequest *requestObj = [NSURLRequest requestWithURL:url];
[_webView loadRequest:requestObj];
}
Now also at the moment the code show I am doing a push navigation, but that is not working - if I embed the first view then I get an error saying I can't push that view twice, so they must be related.
this is the error message for that:
2012-06-08 22:23:28.965 menu_test[68491:f803] nested push animation can result in corrupted navigation bar
2012-06-08 22:23:29.447 menu_test[68491:f803] Finishing up a navigation transition in an unexpected state. Navigation Bar subview tree might get corrupted.
2012-06-08 22:23:29.449 menu_test[68491:f803] Unbalanced calls to begin/end appearance transitions for .
Now even though these seem like 2 questions, they are sure to be related.
Also I have tried playing with NSUserDefaults, but that is not working as smooth, because the link needs to be set before I can use it, rather than directly passing it on.
Any help would be great:-)
What is happen is that you are really pushing the view controller twice, first time is from the storyboard push sequence, and the second time you do it from code,
You have 2 solutions
I will write down the easiest of them (if you need the second too, please ask)
you will need to pass the data from the prepareForSegue method
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([[segue identifier] isEqualToString:#"TheIdentifierOfYourDetailView"]) {
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
int currentindex = [indexPath row];
switch (currentindex) {
case 0:
_urlAddress = #"http://localhost:8888/thePalmsMenu/breakfast_menu.php";
break;
case 1:
_urlAddress = #"http://localhost:8888/thePalmsMenu/lunch_menu.php";
break;
default:
break;
}
[[segue destinationViewController] setUrlAddress:_urlAddress];
}
}
Related
I'm trying to create and set VCs for my tab view controller. There is a next button in each VC that either opens the next tab or loads a new set of tabs when it reaches the end. Inside both Type1.m and Type2.m:
- (IBAction)next:(id)sender {
if (self.tabBarController.selectedIndex < [self.tabBarController.viewControllers count] - 1) {
self.tabBarController.selectedIndex += 1;
}
else {
// load nextData
NSLog(#"nextData: %#", nextData);
if (nextData == nil) {
// essentially stop the next button from working
return;
}
self.tabBarController.selectedIndex = 0U;
[self.tabBarController setTitle:[nextData display_name]];
[self.tabBarController setViewControllers:[nextData loadTabs]];
}
}
loadTabs is a method of the Data class (which is a Core Data NSManagedObject subclass with a category).
Inside ParentData+Helpers.m It looks like:
- (NSArray *)loadTabs {
NSMutableArray *mut = [NSMutableArray new];
for (SubData *d in self.datapoints) {
if ([d.field_x isEqual:#YES]) {
[mut addType1:d];
else {
[mut addType2:d];
}
return mut;
}
addType1 and addType2 are nearly identical in their fields, but have different implementations. They look like:
- (TypeVC *)addType1:(SubData *)data {
TypeVC *vc = [[TypeVC alloc] init];
vc.datapoint = data;
return vc;
}
where TypeVC is either Type1 or Type2
So now I have a table view and each cell has an associated ParentData which has an array of SubData objects. When selected, it loads a UITabViewController with Type1 and Type2 VCs.
inside TableVC.m:
- (void)- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self performSegueWithIdentifier:#"toTabs" sender:self];
}
where toTabs leads to a UITabBarController in the storyboard.
In prepareForSegue:
UITabBarController presentedTabBarController = [segue destinationViewController];
[presentedTabBarController setTitle:self.selectedTitleName];
[presentedTabBarController setViewControllers:[self.selectedParentData loadTabs]];
So all that works fine.
So I run my app, get to the UITabBarController and the thing that should be there is. The first set of tabs has 2 tabs, so I hit next and it goes to the second one. I hit next, and it loads the next set of tabs and opens to the correct tab. I hit next, alls working good. I hit next four or five times, each time it works correctly. It doesnt matter if its Type1 or Type2, it works.
But then suddenly, I get to a specific one that has 2 tabs. It loads the first one (the one at index 0) and then I hit next. And suddenly I have EXC_BAD_ACCESS(code=1, address=0xWHATEVER) at the line self.tabBarController.selectedIndex += 1;. I don't understand why this would happen.
I found a really good walk through of how to pass string values back from a ViewController to a calling ViewController and got it working perfectly. The example is really very good.
https://www.youtube.com/watch?v=YVikeoR3gYg
That said, the technique for passing back content seems relatively straight forward now that I have seen it, even if it's not that intuitive.
The example code however only includes two controllers. When I replicated the code using a much more detailed Storyboard, the code simply doesn't work. In my test app, I even embedded the calling Controller inside a NavigationController to see whether this would have an affect, but it still continued to work fine.
In my application, the ViewController is embedded within a NavigationController that is called via a SWRevealController segue class. I don't know if this is important or relevant but I am mentioning it.
I then call a CollectionViewController to choose an icon that should be passed back to the calling ViewController.
When I select the icon, I correctly identify the icon and pop
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
selectedIcon = [placeIcons objectAtIndex:indexPath.row];
NSLog(#"In IconCollectionViewControlled - selected %#", selectedIcon);
NSString *itemToPassBack = #"12345"; // Just testing any old string here...
// [self.delegate passBackIcon:selectedIcon]; // commenting out while testing
[self.delegate passBackIcon:itemToPassBack];
[self.navigationController popViewControllerAnimated:YES];
}
I get a correct trace suggesting that the right icon is selected. I would then expect that the text '12345' would be passed back to the calling Controller.
In my calling Controller, I have the following:
- (void)passBackIcon:(NSString *)iconName {
NSLog(#"Icon to use is %#", iconName);
}
But this just isn't being called at all (or at least I'm not seeing the NSLog being shown. It's just being ignored.
The delegate is being correctly declared as far as I can tell.
Am I missing something?
assuming you are working with segues, in the method prepareSegue you should setting the delegate
for Example :
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"YOUR_SEGUE_IDENTIFIER"] ) {
DestinationVc *vc = (DestinationVc *)segue.destinationViewController;
[vc setDelegate:self];
}
}
Hope it works for you
I've found this to be the easiest way to pass string and other information around using a tableView.
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ViewControllerYouWantToPassToo *result = [self.storyboard instantiateViewControllerWithIdentifier:#"NameOfTheViewController"];
result.stringName = #"12345" // String Name is a NSString property you set up in the ViewController you want to pass too
[self.navigationController pushViewController:result animated:YES];
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
I suggest you wrapping your delegate in a check to see that it is valid and that it has adopted the respective method (if optional).
if(self.delegate && [self.delegate respondsToSelector:#selector(passBackIcon:)]){
[self.delegate passBackIcon:itemToPassBack];
}else{
NSLog(#"Your delegate is not setup correctly");
}
If it enters the else, you have not set the delegate properly..ie you likely never did
self.delegate = SomeInstanceOfAClassThatAdoptsYourDelegate;
I have two UIViewControllers, one is a UIPickerViewController, the Other a UITableViewController. Ideally the Picker should get a request from the user to add x amount of some item to the tableView. The Picker gets user inputs and assigns them to variables val1, val2, val3, where val1 is the number of items (number of rows) and val2 is the name or label for the item.
PickerViewController.m
- (IBAction)add:(id)sender
{
TableViewController *tvc = [[TableViewController alloc] init];
[tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:tvc animated:YES completion:nil];
}
TableViewController.m
-(void)setValues:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
self.val1 = newVal1;
self.val2 = newVal2;
self.val3 = newVal3;
}
- (void)viewDidLoad
{
[super viewDidLoad];
[self.tableView registerClass:[UITableViewCell class] forCellReuseIdentifier:#"UITableViewCell"];
// This is just a header which holds my "Add" button
UIView *header = self.headerView;
[self.tableView setTableHeaderView:header];
[self addNew:self.val1 :self.val2 :self.val3];
}
- (void)addNew:(NSString *)newVal1 :(NSString *)newVal2 :(NSString *)newVal3
{
if(!self.numberOfRows){
NSLog(#"Initially no of rows = %d", self.numberOfRows);
self.numberOfRows = [self.val1 intValue];
NSLog(#"Then no of rows = %d", self.numberOfRows);
}
else
{
self.numberOfRows = self.numberOfRows + [newVal1 intValue];
NSLog(#"New no rows = %d", self.numberOfRows);
}
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:self.numberOfRows inSection:0];
// Only run when called again .. not initially
if(self.run != 0){
[self.tableView beginUpdates];
[self.tableView insertRowsAtIndexPaths:#[indexPath]withRowAnimation:UITableViewRowAnimationBottom];
self.run ++;
[self.tableView endUpdates];
}
}
// "ADD" button which should go back to the picker and get new items to add to the table
- (IBAction)testAdd:(id)sender
{
PickerViewController *pvc = [[PickerViewController alloc] init];
[self presentViewController:pvc animated:YES completion:nil];
}
Now, I realize every time I call the next view controller I am creating a new instance of it, but I don't know how else to do it, I figure this is the main problem. As of right now, I expect when I leave the tableview for the picker view and return the console should log "New no of rows = x" but that doesn't happen.
I know val3 isn't used and my addNew: may not be the best, but I just need it to handle the basic logging mentioned above and I should be able to take it from there.
Been stuck on this for days
Create a property for TableViewController, and only create it the first time you present it,
- (IBAction)add:(id)sender {
if (! self.tvc) {
self.tvc = [[TableViewController alloc] init];
}
[self.tvc setValues:self.val1 :self.val2 :self.val3];
[self presentViewController:self.tvc animated:YES completion:nil];
}
It's not entirely clear from you question, whether it's this presentation or the one you have in the table view class that you're talking about. It also looks like you're doing something wrong in terms of presentation -- you're presenting the picker view from the table view controller, and also presenting the table view controller from the picker. That's not correct, you should present which ever controller you want to appear second, and that controller should use dismissViewControllerAnimated to go back, not present another controller.
In testAdd you don't need to create a new instance and present it. If you want to go back to the presentingViewController, just use dismissViewControllerAnimated .
And you will go one controller up in the stack.
I am developing a small music application in which first view comprises of all the List of songs in Tableview and when user taps on any one row (song) it takes them to Second view & Plays the song their(comprising of play/Pause Buttons). Now I go back in First View from Second View using back button to select another song. And their is one button in First View for just switching views(i.e it takes to current playing song)but it plays that same song from start but not from its currentplaying . Its similar to now Playing button in Stock Music app in iOS.
Im using storyboards & Segues
-(void) tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
temp = indexPath.row;
[tableView deselectRowAtIndexPath:indexPath animated:NO];
[self performSegueWithIdentifier:#"Player" sender:self];
}
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if([[segue identifier] isEqualToString:#"Player"])
{
MPMediaItem *item = [itemCollections objectAtIndex:temp];
NSString *trackInfo = [[NSString alloc] initWithFormat:#"%#- %#",[item valueForProperty:MPMediaItemPropertyTitle],[item valueForProperty:MPMediaItemPropertyArtist]];
MPMediaItemArtwork *artwork = [item valueForProperty:MPMediaItemPropertyArtwork];
NSURL *tempURL = [item valueForProperty:MPMediaItemPropertyAssetURL];
playerController = [segue destinationViewController];
playerController.fileUrl1 = tempURL;
playerController.globalVar = [NSNumber numberWithInteger:temp];
playerController.labelTitleString = trackInfo;
playerController.img = [artwork imageWithSize:CGSizeMake(100, 100)];
}
}
You could go about this in many ways -- I would suggest implementing a singleton to handle the media playback. Rather than constantly retaining that view, this singleton manager (part of your model) should provide any controllers with the needed information about the current song, album, author, etc.
Ex. In your TableViewController:
- (void)viewDidLoad
{
[self setSong:[[PlaybackManager sharedInstance] currentSong]];
if (self.song) {
//Add navigation item
}
}
I'm developing a simple app which downloads images from Dribbble, but I'm having problem reloading the data for my collection view. I have two views set up with ViewDeck, center is my main view which contains the collection view and another view contains table view with settings and from there I'm trying to call a method in the first view and reload data when item is tapped but it just doesn't work.
I tried to call the same method from the main window using button -> worked like a charm but from the second window it just doesn't update the data.
I tried to debug somehow and seems like my collection is null when the reload is called, no idea why.
SettingsViewController
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"tap");
JKViewController *appDelegate = [[JKViewController alloc] init];
appDelegate.dataHasChanged = YES;
[appDelegate refresh];
[self.viewDeckController closeLeftViewAnimated:YES];
}
MainView
- (void)refresh{
NSLog(#"refresh");
if(dataHasChanged)
{
switch (listType) {
case 0:
[self refreshWithList:SPListPopular];
break;
case 1:
[self refreshWithList:SPListEveryone];
break;
case 2:
[self refreshWithList:SPListDebuts];
break;
case 3:
[self refreshWithList:SPListPopular];
break;
default:
[self refreshWithList:nil];
break;
}
dataHasChanged = NO;
NSLog(#"Should refresh");
}
NSLog(#"%d", [self->shots count]);
NSLog(#"Collection view: %#",self.collectionView.description);
NSLog(#"self.list: %#",self.list);
NSLog(#"List type: %d", listType);
}
This doesn't work :/, but when I call it from button in the MainView it works.
- (IBAction)changeList:(id)sender {
[self refreshWithList:SPListDebuts];
}
Does anyone know what could be the issue?
Edit - Solved
Getting the right instance of the centerViewController
JKViewController *mainController = ((UINavigationController*)self.viewDeckController.centerController).visibleViewController.navigationController.viewControllers[0];
The reason that you are not seeing your data being updated is because you are creating a new view controller and telling that to refresh. This new view controller has been initialized but not added to your view hierarchy. What you want to do is message the existing view controller like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"tap");
JKViewController *mainViewController = self.viewDeckController.centerViewController;
mainViewController.dataHasChanged = YES;
[mainViewController refresh];
[self.viewDeckController closeLeftViewAnimated:YES];
}
Also, please note that I have changed the variable name in my revision. Naming a UIViewController instance 'appDelegate' is very confusing.