DetailViewController Not Updating For Animation - ios

I have a UISplitview controller (with a master and detail view). So, when the detailview loads the viewDidLoad calls a function named [self animateToPosition]; with the following code:
-(void*)animateToPosition
{
NSLog(#"Called Function");
[worldV animateToPosition:MaplyCoordinateMakeWithDegrees(-122.4192, 37.7793) time:1.0];
[self.view setNeedsDisplay];
return 0;
}
Now, when this is first run, my globeViewC correctly animates to the position passed in (worldV is initialized and created an all in viewDidLoad of detailviewController also "Called Function" is printed in the log.
However, I have a view controller and in the didSelectRowAtIndexPath I have the following code (in the file masterViewController.m)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([[testArray objectAtIndex:indexPath.row]isEqualToString:#"Menu1"]) {
DetailViewController *test=[[DetailViewController alloc]init];
[self addChildViewController:test];
[self.view addSubview:test.view];
[test didMoveToParentViewController:self];
[test animateToPosition];
}
}
When I select the cell called menu1, "Called function" DOES display in the NSLog, but the animation that occurred when the view was first loaded doesn't anymore. I'm wondering if I must somehow reload the detailviewcontroller or initialize it again (but I tried initializing it again and that didn't work)
Would like if someone could see this and help me get this working.

It would be helpful if you could show more, especially how you alloc and init your worldV and what you have in your viewDidLoad.
Normally you don't create a new DetailViewController for SplitView setup.
Instead of creating a new DetailViewController, try to grab a hold of the existing detail view controller by creating a property:
#property(strong, nonatomic) DetailViewController *detailViewController;
and:
self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController];
First thing to test here is whether your worldV is nil. A simple NSLog in animateToPosition would reveal the answer.
And whenever you need to call that method, do:
[self.detailViewController animateToPosition];
To animate from one coordinate to another with time, you may need to change your animateToPosition method to:
-(void)animateToPosition:(MaplyCoordinate)coordinates time:(CGFloat)time
Consult whether or not your MaplyCoordinate is an object, which I doubt.
So, to sum up, in your tableview's viewDidLoad, get a hold of the detail view controller.
When didSelectRow gets called, use the new method suggested and pass the coordinate and time this way:
[self.detailViewController animateToPosition:coordinates time:1.0];
Hope this helps.

In didSelectRowAtIndexPath function you create an DetailViewController but you don't add it to the view hierarchy. You should add it to the parent view controller like this
[self addChildViewController:viewController];
[self.view addSubview:viewController.view];
[viewController didMoveToParentViewController:self];
or just add it to navigation or present it as a modalViewController

Related

Reloading data for collectionview

I have 2 ViewControllers
ViewControllerWithCollectionView (FIRST) and ModalViewControllerToEditCellContent (SECOND)
I segue from FIRST to SECOND modally. Edit cell. Return.
After dismissing SECOND controller, edited cell doesn't get updated until i call
[collection reloadData]; somewhere manually.
Tried to put it in viewWillAppear:animated:, when i check log, it's not called (after dismissing SECOND)
I've tried various solutions, but i can't brake thru (maybe I'm just too exhausted). I sense that I'm missing something basic.
EDIT dismiss button
- (IBAction)modalViewControllerDismiss
{
self.sticker.text = self.text.text; //using textFields text
self.sticker.title = self.titleText.text;// title
//tried this also
CBSStickerViewController *pvc = (CBSStickerViewController *)self.stickerViewController;
//tried passing reference of **FIRST** controller
[pvc.cv reloadData];//called reloadData
//nothing
[self dismissViewControllerAnimated:YES completion:^{}];
}
It's tough to tell from the posted code what's wrong with the pointer to the first view controller that you passed to the second. You should also be able to refer in the second view controller to self.presentingViewController. Either way, the prettier design is to find a way for the first view controller to learn that a change has been made and update it's own views.
There are a couple approaches, but I'll suggest the delegate pattern here. The second view controller can be setup to have the first view controller do work for it, namely reload a table view. Here's how it looks in almost-code:
// SecondVc.h
#protocol SecondVcDelegate;
#interface SecondVC : UIViewController
#property(weak, nonatomic) id<SecondVcDelegate>delegate; // this will be an instance of the first vc
// other properties
#end
#protocol SecondVcDelegate <NSObject>
- (void)secondVcDidChangeTheSticker:(SecondVc *)vc;
#end
Now the second vc uses this to ask the first vc to do work for it, but the second vc remains pretty dumb about the details of the first vc's implementation. We don't refer to the first vc's UITableView here, or any of it's views, and we don't tell any tables to reload.
// SecondVc.m
- (IBAction)modalViewControllerDismiss {
self.sticker.text = self.text.text; //using textFields text
self.sticker.title = self.titleText.text;// title
[self.delegate secondVcDidChangeTheSticker:self];
[self dismissViewControllerAnimated:YES completion:^{}];
}
All that must be done now is for the first vc to do what it must to be a delegate:
// FirstVc.h
#import "SecondVc.h"
#interface FirstVc :UIViewController <SecondVcDelegate> // declare itself a delegate
// etc.
// FirstVc.m
// wherever you decide to present the second vc
- (void)presentSecondVc {
SecondVc *secondVc = // however you do this now, maybe get it from storyboard?
vc.delegate = self; // that's the back pointer you were trying to achieve
[self presentViewController:secondVc animated:YES completion:nil];
}
Finally, the punch line. Implement the delegate method. Here you do the work that second vc wants by reloading the table view
- (void) secondVcDidChangeTheSticker:(SecondVc *)vc {
[self.tableView reloadData]; // i think you might call this "cv", which isn't a terrific name if it's a table view
}

Update tableView data

I have a UITableView embedded in a UIView that I can't figure out how to update. When I come to this page with the UITableView embedded in the UIView there's a button that when pressed brings up a modalForm. There is a textField that the user enters a name into and then presses another button to "Create" the object, dismiss the modalForm, and update the app data. However, I'm not sure how to refresh my UITableView data… Is there a way to tell the UITableView to refresh when the modalForm view is dismissed?
EDIT: I know i need to send this message [tableView reloadData]; in order to refresh, but I'm wondering where I can put it so it gets called upon dismissal of the modalForm?
In the view controller that is presenting and dismissing the modal, you should be calling the dismissViewControllerAnimated:completion method to dismiss the modal. The modal should not dismiss itself. You can use a completion block to execute whatever code you would like that will execute when the modal is finished dismissing. Example below.
[self dismissViewControllerAnimated:YES completion:^{
//this code here will execute when modal is done being dismissed
[_tableView reloadData];
}];
Of course don't forget to avoid capturing self strongly in the block.
If you end up having the modal dismiss itself, you will need either a delegate method so the modal can communicate back to the presenting view controller, or a notification sent by the modal and captured by the presenting view controller, or you could implement viewWillAppear: in the presenting view controller. This method will fire everytime the view is about to appear. Which means the very first time as well as after a modal is dismissed and is about to show the view it was presenting over top of.
----------------------------------------------------------------------------------------
Below is an example of writing your own protocol and using it.
MyModalViewController.h
#protocol MyModalViewControllerDelegate <NSObject>
//if you don't need to send any data
- (void)myModalDidFinishDismissing;
//if you need to send data
- (void)myModalDidFinishDismissingWithData:(YourType *)yourData
#end
#interface MyModalViewController : UIViewController
#property (weak) id <MyModalViewControllerDelegate> delegate;
//place the rest of your properties and public methods here
#end
The where ever you want to in your MyModalViewController implementation file, call your delegate method of choice. You should first make sure your delegate actually responds to the selector though.
MyModalViewController.m
if ( [self.delegate respondsToSelector:#selector(myModalDidFinishDismissing)] )
[self.delegate myModalDidFinishDismissing];
In your viewcontroller that is presenting the modal, you need to state in the header file that you conform to the protocol, you need to set the delegate of the modal as the viewcontroller, and make sure you actually implement the delegate method you intend on using.
MyPresentingViewController.h
#interface MyPresentingViewController : UIViewController <MyModalViewControllerDelegate>
MyPresentingViewController.m
myModal.delegate = self;
- (void)myModalDidFinishDismissing {
//do something
[tableView reloadData];
}
You can have a delegate variable in your modal form class. When you dismiss your modal form, you can call something like [delegate performSelector:#selector(modalFormClosed)] which calls the [tableView reloadData].
#interface ModalForm : UIViewController
#property (nonatomic, weak) id delegate;
#end
#implementation ModalForm
- (void)viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[self.delegate performSelector:#selector(modalFormClosed)];
}
#end
In your class that uses the ModalForm:
myModalForm.delegate = self;
And make sure you have the modalFormClosed method too:
- (void)modalFormClosed {
[self.tableView reloadData];
}
Or you can send a NSNotification (look into NSNotificationCenter) when your modal form disappears.
I wish I had the reference, but some years ago I saw a note from Apple saying a modal view should never dismiss itself. Instead you call a method on a presenting view controller. In this method the presenter dismisses the modal view controller. Typically I put a category on my UIViewControllers:
#interface UIViewController (Extension)
-(void)simpleDismissAnimated:(BOOL)animated;
#end
#implementation UIViewController (Extension)
-(void)simpleDismissAnimated:(BOOL)animated {
[self dismissViewControllerAnimated:animated completion:nil];
}
Either include it in all your view controller subclasses, or in your pch file.
So have your modal view controller call:
[self.presentingViewController simpleDismissAnimated:YES];
Then in your presenting view controller, override the implementation of simpleDismissAnimated: to something like:
-(void)simpleDismissAnimated:(BOOL)animated {
[_tableView reloadData];
[self dismissViewControllerAnimated:animated completion:nil;
}
That should take care of it.
Try this line:
[self.tableView reloadData];
You can try putting [tableView reloadData] in viewWillAppear method where your tableView is.
I had the same problem. ReloadData didn't work me. I solved it so that i made new method like this:
- (void)insertNewString:(NSString *)text{
if (!_yourArrayWithData) {
_yourArrayWithData = [[NSMutableArray alloc] init];
}
[_yourArrayWithData insertObject:text atIndex:0];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:0 inSection:0];
[self.tableView insertRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationAutomatic];
}
and when i want to add something to tableView, i use button with action like this:
- (IBAction)SaveNewPhrase:(id)sender {
NSString *mainText = _NewPhraseTextView.text; //I take nsstring from textView
if([mainText isEqual:#""]){
return;
}else{
NSMutableArray *contentFile = [NSArray arrayWithContentsOfFile:docPathContents()];
NSMutableArray *copyContentFile = [[NSMutableArray alloc] init];
//save array with new item
[copyContentFile addObject:mainText];
[copyContentFile addObjectsFromArray:contentFile];
[copyContentFile writeToFile:docPathContents() atomically:YES];
[self insertNewString:mainText]; //refresh tableView
[_NewPhraseTextView resignFirstResponder];
}}
I hope, that it helps.

UITableView Passing Data Between Controllers Embedded Inside Containers

I have a simple test project. A UITableViewController (MasterViewController) embedded inside a navigation controller in storyboard. I am NOT segueing using the prepareForSegue to pass data to another view controller (DetailViewController). Instead, didSelectRowAtIndexPath is use to update a label in the detailviewcontroller as below:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [self.storyboard instantiateViewControllerWithIdentifier:#"DetailViewController"];
NSMutableString *object = thisArray[indexPath.row];
detailViewController.passedData = object;
[self.navigationController pushViewController:detailViewController animated:YES];
}
Everything till this point works fine.
Now i have added another view controller in my storyboard. Made it the initial view controller, added two containers in it, then embedded both MasterViewController and DetailViewContainer in these containers.
Now instead of showing passed data inside the DetailViewController on the right side, its showing the passed data on the left side by replacing the controller view.
If i am not able to clarify what i am trying to say, here is the link to the project https://jumpshare.com/v/UiTFEB6AamIo8qX9sinW , its just for learning purpose.
Thanks
You're getting this problem because you are still doing this:
[self.navigationController pushViewController:detailViewController animated:YES];
The navigation controller you're referencing here is the one your master controller is embedded in, so you create an instance (different than the one that's already on screen) of detailController and push that onto the navigation controller.
What you want to do, is get a reference to the detail controller that's already on screen -- both child view controllers (the ones in the container views) are already instantiated when the app starts. So, you need to do this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
DetailViewController *detailViewController = [self.navigationController.parentViewController childViewControllers][1];
NSMutableString *object = thisArray[indexPath.row];
detailViewController.passedData = object;
}
This will pass the value to the detail controller, but you can't have the code to update the label in viewDidLoad, since that view is already loaded, and won't be called again. Instead, override the setter for passedData, and update the label there (note that I changed the name of the argument to passedInData, so it doesn't conflict with your property passedData):
-(void)setPassedData:(NSString *)passedInData {
passedData = passedInData;
detailDescriptionLabel.text = passedData;
}
Bu the way, unless you're planning on adding other controllers after your master view controller, there's no reason to have it embedded in a navigation controller at all, given this set up. If you take it out, then you need to remove the reference to self.navigationController when you get the reference to the detail controller. It would then just be:
DetailViewController *detailViewController = [self.parentViewController childViewControllers][1];

How can I show a new view from the UITableViewController contained my UIViewController?

I have a UITableViewController within a UIViewController. While this table viewcontroller was the only one involved, it was pushing views just fine when the user would tap a row. However, ever since I moved it to be one of two contained within the UIViewController, the taps of rows suddenly do nothing.
I've tried searching around and I'm not the first to run into this problem, but none of the answers fit my circumstances or the questions have no working answers. That link was the closest I found, but I'm not using storyboards -- I'm using separate XIBs.
So how do I push a new view from a viewcontroller within a viewcontroller?
To recap:
Here is what I had, and it worked fine in taking users to a new screen!
// Normal table behavior, as illustrated by [another question][2].
- (void)tableView:(UITableView *)tableView
didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
SomeView *detailViewController = [[SomeView alloc] initWithNibName:#"SomeView" bundle:nil];
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
}
Now I have the viewcontroller as a property in a view -- and the above code, which is in the file for the tableviewcontroller and not at the "main" view, doesn't cause a new screen to appear anymore!
Thanks for the comments! Here's some code to clarify my scenario.
The controllers within a controller. This is a file from a test project I've been using to test the concept out. In this case, I have a tableview controller within a tableview controller.
#interface SimpleTableViewController : UIViewController <UITableViewDelegate, UITableViewDataSource>
// This is the controller within the controller
#property IBOutlet SecondTableViewController *secondTableController;
#property IBOutlet UITableView *secondTable;
My SecondTableViewController has this fun bit.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here. Create and push another view controller.
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"SimpleNonTableViewController" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[manualViewControllerParent.navigationController pushViewController:detailViewController animated:YES];
}
The view that the user interacts with is hooked up to SimpleTableViewController. In this way, SecondTableViewController is "within" SimpleTableViewController. Feel free to comment if you'd like more details!
I've put my test/concept project on github. https://github.com/hyliandanny/TableViewCeption
You need to use a custom container controller to do what you want. It would be easiest if you used a storyboard, but you can do it in code with xibs as well. The outer controller should be a UIViewController, not a table view controller. You can do something like this (in the outer controller):
- (void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
UIViewController *detailViewController = [[UIViewController alloc] initWithNibName:#"SimpleNonTableViewController" bundle:nil];
[self addChildViewController:detailViewController];
detailViewController.view.frame = set the frame to what you want;
[self.view addSubview:detailViewController.view];
[detailViewController didMoveToParentViewController:self];
}
You should read up on Apple's documentation for custom container controllers.
What you need to make sure:
Your UITableView delegate is hooked up to your controller. Otherwise it wouldn't call didSelectRow. You can do this in xib or in viewDidLoad method.
Your self.navigationController is not nil
Your detailViewController is not nil
I also think that what you mean is you have UITableView inside your UIViewController. UITableView is only the view, whereas UITableViewController is a controller. You can't have a controller inside another controller.

UIWindow not getting set when pushing UIViewController from Nib

I'm trying to do a very simple pushViewController with a view controller created from a nib.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
ServiceDetailViewController *serviceDetail = [[ServiceDetailViewController alloc] initWithNibName:#"ServiceDetailViewController" bundle:nil];
serviceDetail.employee = _employee;
[self.navigationController pushViewController:serviceDetail animated:YES];
previousSelectedRow = indexPath;
}
If I return the window of the serviceDetail view controller within its viewDidLoad or anywhere else inside of its functions it is null. When I return its window right after pushViewController it is fine.
My viewDidLoad is normal. I am calling super.
It seems like this is either something silly that I'm overlooking, a problem with my splitViewController setup, or a bug in xCode 4/ARC.
I understand I may need to provide a lot more code but I'm hoping someone might have an idea.
viewDidLoad is called right after a view is loaded into memory, but before the view is added to the view hierarchy including the backing window. The viewDidAppear method is called right after being added to a window. Maybe your code needing the window value will work better in that method.

Resources