반응형
Thread Tools Search this Thread Display Modes
Old Dec 17, 2011, 03:03 PM #1
tutiplain
macrumors member
Join Date: Feb 2011
refresh a tableview after popping from a navigation stack
Hi all,
I have two UIViewControllers inside a navigation interface. The first one has a UITableView inside it (The view controller itself is both datasource and delegate). When one particular row is touched, the navigation view pushes the second view into the stack. This second view presents another UITableView with some options.
When one option is selected in the second view controller, the navigation pops the second view controller out of the navigation stack, but not before setting a property in the first navigation item that will be displayed in another section of the table View in the first. The second view controller also calls the reloadData on the first view controller's table view before popping. The problem, however, is that the first view controller's data is never refreshed.
Here is what happens on the second view controller when an item is tapped:
Code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.action ==@"set_active") {
FirstViewController *firstMenu = (FirstViewController*) [self.navigationController.viewControllers objectAtIndex:[self.navigationController.viewControllers count]-2];
Scheme* selectedScheme = [self.theSchemes objectAtIndex:indexPath.row];
firstMenu.activeScheme = selectedScheme;
[firstMenu.tableView reloadData]; //this method doesn't seem to work
[self.navigationController popViewControllerAnimated:YES];
}
By debugging I discovered that, indeed, the activeScheme property has a value after this method call runs, so the line before the call to reloadData runs successfully. However, calling reloadData here does nothing. I even set up a breakpoint in the first view controller's cellForRowAtIndexPath: method, and the app never stopped on it.
As an alternative approach, I tried to call the reloadData method on the first view's viewWillAppear: method. Nothing happened.
I also tried using a CADisplayLink to call the reloadData method after popping the second view, and it still doesn't work.
I know I have my tableview set up correctly since it initially shows what it's supposed to. I just don't know why it won't refresh afterwards. Here is the code for the cellForRowAtIndexPath: method:
Code:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.identifier){
self.identifier = [NSString stringWithFormat:@"MyIdentifier"];
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.identifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.identifier];
}
if (indexPath.section ==0)
{
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [taskOptions objectAtIndex:indexPath.row];
}
else
{
cell.textLabel.text = self.activeScheme.name ; //this is the cell where the selected value is supposed to appear
}
return cell;
}
Last edited by tutiplain; Dec 17, 2011 at 05:43 PM. Reason: needed to subscribe
tutiplain is offline 0 Reply With Quote
Old Dec 31, 2014, 05:19 PM
Sponsored
Old Dec 17, 2011, 05:52 PM #2
North Bronson
macrumors 6502
Join Date: Oct 2007
Location: Los Angeles
It is not really a great design pattern. You should not make your Detail View Controller poke through the navigation controller stack to find the Master View Controller.
Your Master View Controller should create a Detail View Controller when necessary and set itself as the delegate object. The Detail View Controller should define a protocol of methods for when interesting events happen. The Master View Controller should conform to that protocol.
When the user updates your data, Detail View Controller sends a message to its delegate object. The Master View Controller handles the delegate method appropriately.
__________________
North Bronson Software
North Bronson is offline 0 Reply With Quote
Old Dec 17, 2011, 07:10 PM #3
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
Hi. Thanks for your reply. I haven't actually yet looked into protocol creation (though I've used protocols to program table views), but it seems like a good way to handle it. I will definitely look into it. Still, is there any particular reason why my approach doesn't work?
tutiplain is offline 0 Reply With Quote
Old Dec 17, 2011, 08:15 PM #4
jonnymo5
macrumors 6502
Join Date: Jan 2008
Location: Texas
Wirelessly posted (Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3)
Never tried it but it is extremely fragile even if it did work. As NB said you should set up a protocol and delegate. You don't want your subviews to require specific knowledge about who created them and how deep they are in the navigation. Learning the new design pattern will end up being faster than debugging it
jonnymo5 is offline 0 Reply With Quote
Old Dec 19, 2011, 10:23 PM #5
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
Hi again,
I read up a bit on protocols on the apple developer site, but I am not quite sure how declaring a protocol is going to help me solve the problem. Is there some example you could direct me to, that shows this at work? It would be much appreciated, thanks!
tutiplain is offline 0 Reply With Quote
Old Dec 19, 2011, 11:23 PM #6
bweberapps
macrumors member
Join Date: Jun 2010
To answer the original question, if the data loaded into the table is already updated by your detail view then just put a reload data call in the viewwillappear function on the table view controller. The viewwillappear method gets called, but not the viewdidload, after a pop. Hope that helps.
__________________
Check out Marble Marathon on the App Store. It puts a whole new twist on the typical Labyrinth type game!http://bweberapps.wordpress.com
bweberapps is offline 0 Reply With Quote
Old Dec 19, 2011, 11:27 PM #7
jonnymo5
macrumors 6502
Join Date: Jan 2008
Location: Texas
When you define a protocol for that second view controller you are giving it a way to control any class that implements it. The second view controller then can force its delegate to run one of the methods in its protocol.
So when the first view controller instantiates the second one it can set itself as the delegate for the second one before pushing it on the navigation stack. Then you just put your update code in the method the first view controller had to implement to conform to the protocol. That lets you pass variables back to the first controller without the second one having to know specific details about it. All the second view controller knows is it has some delegate that has promised to respond to its protocol methods.
As for examples look at the table view delegate. You have to implement the methods that provide cells and the table view has to provide you with the index path it wants the info about.
jonnymo5 is offline 0 Reply With Quote
Old Dec 20, 2011, 09:51 PM #8
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
Hi again.
So I did a bit poking around, and found a way to implement this in a protocol, but I still can't get the table to refresh.
I defined my protocol like this:
Code:
@protocol ActiveSchemeSelector <NSObject>
@required
-(void)selectedActiveScheme:(Scheme *)newActiveScheme;
@end
Note that Scheme is a custom class for my app, part of my data model. I then modified the second view controller (the Detail View, as some of you call it) and added a property like this:
Code:
@interface SecondViewController : UIViewController <UITableViewDataSource,UITableViewDelegate>
{
NSString *action;
id <ActiveSchemeSelector> schemeSelectorDelegate;
}
@property(nonatomic,retain) NSString *action;
@property(assign) id schemeSelectorDelegate;
Modified the didSelectRowAtIndexPath method like this:
Code:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if (self.action ==@"set_active") {
Scheme* selectedScheme = [self.theSchemes objectAtIndex:indexPath.row];
[schemeSelectorDelegate selectedActiveScheme:selectedScheme]; //this is the protocol method
[self.navigationController popViewControllerAnimated:YES];
}
}
Finally, I added the following to my FirstViewController's definition:
Code:
@interface FirstViewController : UIViewController <UITableViewDataSource, UITableViewDelegate,ActiveSchemeSelector>
And added the following code to the implementation of the first view controller:
Code:
-(void)selectedActiveScheme:(Scheme *)newActiveScheme
{
self.activeScheme =newActiveScheme;
NSLog(self.activeScheme.name); //shows the name of the selected Scheme object, as selected in the second view controller
[taskOptionsTableView reloadData]; //this line doesn't do anything
}
With that NSLog, I make certain, that, indeed, the correct value was assigned to self.activeSheme. However, calling [tableView reloadData] does not cause the name to be displayed there. So while things are better designed now, it still doesn't do what I want it to. What can I possibly be missing?
tutiplain is offline 0 Reply With Quote
Old Dec 20, 2011, 09:58 PM #9
North Bronson
macrumors 6502
Join Date: Oct 2007
Location: Los Angeles
Quote:
Originally Posted by tutiplain View Post
I defined my protocol like this:
Code:
@protocol ActiveSchemeSelector <NSObject>
@required
-(void)selectedActiveScheme:(Scheme *)newActiveScheme;
@end
It is usually the custom that your delegate methods also pass the object that is doing the calling:
Code:
- (void)detailViewController:(DetailViewController *)detailViewController didSelectDetailModel:(DetailModel *)detailModel;
This gives you a handy way to query the main object if you need some additional properties that the small object does not provide.
Why are your view controllers not descended directly from UITableViewController?
__________________
North Bronson Software
North Bronson is offline 0 Reply With Quote
Old Dec 20, 2011, 11:07 PM #10
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
I guess I learned "the long way" of creating and using table views. UITableViewController gives some functionality for free, so you don't have to manually specify a delegate and data source. But I read in another forum that using UITableViewController also limits the customizations you can do to the way the table view paints itself. Of course, I haven't tried this myself. In any case, my approach is that UIViewControllers implement the UITableViewDelegate and UITableViewDataSource protocols, and the view controller itself is set as both data source and delegate in Interface Builder. This approach is covered in the Apple docs, so I guess it must be acceptable.
tutiplain is offline 0 Reply With Quote
Old Dec 20, 2011, 11:28 PM #11
North Bronson
macrumors 6502
Join Date: Oct 2007
Location: Los Angeles
Yes. It is true that using UITableViewController will limit the amount of advanced functionality that you achieve. Yes. It is true that certain users might want to use their own view controller. Is it right for you? Will it unnecessarily complicate things more than it will help? Will it stand in the way of your learning? Potentially.
I would guess that the problem has something to do with the way that you are creating the table or saving it to an instance variable. Using the UITableViewController will do that for free and let you focus on the bigger picture of making these two view controllers speak to each other. This is the bigger fish for you to fry.
Try using two UITableViewController subclasses and I bet your problem will vanish.
Either that or something is wrong with the method you use to customize your cells.
__________________
North Bronson Software
North Bronson is offline 0 Reply With Quote
Old Dec 21, 2011, 10:17 AM #12
bweberapps
macrumors member
Join Date: Jun 2010
You don't need to subclass uitableviewcontroller for it to work. I have implemented exactly what he is trying to do many times. I assume the data used in the main table is global and edited by the detail view and saved. All you need to do then is put one line in your main table class to get it to refresh. In the viewdidload method of your main view put [yourtable relaodData]; that is it. No protocols needed. I have this working in an app I am currently working on. The reason your protocol isn't working is because the detail view still has focus when the protocol function is called. If you reload the table data AFTER the detail view has popped and when the main view comes back into focus it will actually be able to refresh the table data. Please give it a try.
Edit: I saw you did try the viewwillappear method, but maybe try it with the protocol to update the selectedSystem instance variable. I also think a modal view might be a better design choice for your scheme selection view. They work great with protocols, but you will still need to refresh the table data when the view will appear. I also have something similar to that working in one of my apps, but I use a global data source instead of an instance variable to put data into my tables.
__________________
Check out Marble Marathon on the App Store. It puts a whole new twist on the typical Labyrinth type game!http://bweberapps.wordpress.com
Last edited by bweberapps; Dec 21, 2011 at 10:43 AM.
bweberapps is offline 0 Reply With Quote
Old Dec 21, 2011, 11:05 PM #13
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
Hi all,
Making my master Detail view a subclass of UITableViewController, plus implementing the protocol solved the problem. Now, my table displays the info selected in Detail View in the last row of the Master, which is what I wanted.
Still, there is something to be learned here, and I'd like to know what it is. I saved my original code, just in case. I decided to compare the cellForRowAtIndexPath methods of each:
The original:
Code:
-(UITableViewCell *) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.identifier){
self.identifier = [NSString stringWithFormat:@"MyIdentifier"];
}
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:self.identifier];
if (!cell)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:self.identifier];
}
if (indexPath.section ==0)
{
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [taskOptions objectAtIndex:indexPath.row];
}
if (indexPath.section ==1) {
cell.textLabel.text = self.activeScheme.name ;
}
return cell;
}
And the new one, based on the template generated by XCode:
Code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = @"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] autorelease];
}
if (indexPath.section ==0)
{
cell.accessoryType=UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [taskOptions objectAtIndex:indexPath.row];
}
if (indexPath.section ==1) {
cell.textLabel.text = self.activeScheme.name ;
}
return cell;
}
Could the source of my problem have been the way I am handling the reuseIdentifier in my original code?
tutiplain is offline 0 Reply With Quote
Old Dec 22, 2011, 12:14 AM #14
North Bronson
macrumors 6502
Join Date: Oct 2007
Location: Los Angeles
Quote:
Originally Posted by tutiplain View Post
Making my master Detail view a subclass of UITableViewController, plus implementing the protocol solved the problem. Now, my table displays the info selected in Detail View in the last row of the Master, which is what I wanted.
You are doing well. Can you tell me why this fixed the bug? Tell me what it was about your old view controller that was not working. Go back and forth and try to make them both work.
Create an application with a tab bar controller. Make one tab be your UITableViewController subclass. Make the other tab be your UIViewController subclass. Use the same Detail Controller class for each one. Work on the code so that both tabs are functionally equivalent.
__________________
North Bronson Software
North Bronson is offline 0 Reply With Quote
Old Dec 22, 2011, 06:38 PM #15
PhoneyDeveloper
macrumors 68030
PhoneyDeveloper's Avatar
Join Date: Sep 2008
Most likely the problem was that you weren't assigning the tableview ivar or it wasn't connected in IB, so its value was nil. You should do it the same way that UITableViewController does it. So you should have a property named tableView and you should refer to it as self.tableView wherever you need to access that ivar in your code. This way you can more easily copy/paste code from UITableViewController subclasses to UIViewController subclasses.
Another suggestion: if you are going to write a UIViewController subclass that is like a UITableViewController when you choose File > New create a UITableViewController subclass. Then change the base class to UIViewController. This way you get all the UITableViewController methods in the file without having to copy them from somewhere else or type them in yourself.
The way you were managing the reuse identifier was certainly overly complicated but didn't look wrong. I always use a simple #define for that.
In your cellForRowAtIndexPath method you need to set all the properties of the cell that might change every time a cell is returned. So in your case you need to always set the cell.accessoryType, which you aren't doing.
PhoneyDeveloper is offline 0 Reply With Quote
Old Dec 23, 2011, 01:46 PM #16
tutiplain
Thread Starter
macrumors member
Join Date: Feb 2011
Hi, thanks again for all the replies. I'd say this has been one of my posts that has proven most insightful so far, and I've learned a lot from each and every reply.
Quote:
You are doing well. Can you tell me why this fixed the bug? Tell me what it was about your old view controller that was not working. Go back and forth and try to make them both work.
Thanks! I cannot yet say why the bug is fixed. I've been looking over the differences in the table view methods on both approaches, but still have yet to find a reason. I will kepp going back and forth as you suggest and see if I can pin it down. It might be working now, but for future references, I might need to know exactly why.
I will post back what I find, or share more of the code so perhaps you guys might be able to help me track it down.
Again, Thanks to everyone, and hope you all have a nice Christmas.
tutiplain is offline 0 Reply With Quote
Old Dec 26, 2011, 12:33 PM #17
moonline
macrumors newbie
Join Date: Dec 2011
Quote:
Originally Posted by North Bronson View Post
You are doing well. Can you tell me why this fixed the bug? Tell me what it was about your old view controller that was not working. Go back and forth and try to make them both work.
Create an application with a tab bar controller. Make one tab be your UITableViewController subclass. Make the other tab be your UIViewController subclass. Use the same Detail Controller class for each one. Work on the code so that both tabs are functionally equivalent.
very nice
moonline is offline
반응형
'모바일개발(Mobile Dev) > IOS개발(ObjectC)' 카테고리의 다른 글
아이폰 코딩 총정리 (0) | 2015.01.14 |
---|---|
Fixing file 'project.pch' has been modified since the precompiled header was built error in Xcode (0) | 2015.01.14 |
멀티태스킹 관련 & C++ basic (0) | 2015.01.14 |
iOS 개발 테스트를 Device로 하기 위한 Device등록 및 App ID, Development Provisioning Profile 생성 (0) | 2015.01.14 |
iOS 개발 인증서 만들어 요청 및 설치까지 (0) | 2015.01.14 |