tableView didSelectRowAtIndexPath doesn't work properly on iOS 7. Why? - ios

In first place I would like to say that I'm only making this question, because I would like to understand what is happening.
I opened an old Xcode project (a very simple one) in a fresh installation on Xcode5.
When I realize that it doesn't work on iOS 7. Why? Don't know..
I saw some other questions, didn't get any useful answer, so I made a simple test.
In the UITableViewController all works fine except on didSelectRowAtIndexPath
Check it out
RootViewController.h:
#interface RootViewController : UITableViewController
#property (strong, nonatomic) NSMutableArray *items;
#end
RootViewController.m
In viewDidLoad I init the array (with some strings).
Implement the dataSource and delegate methods (and yes I set the delegate and dataSource to the tableView)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_items count];
}
- (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];
}
// Configure the cell...
cell.textLabel.text = [_items objectAtIndex:indexPath.row];
return cell;
}
The problem is here:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
// Create the next view controller.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:NO];
detailViewController.detailLabel.text = [_items objectAtIndex:indexPath.row];
}
DetailViewController is a simple UIViewController:
(yes, I set the IBOutlet on nib file)
#interface DetailViewController : UIViewController
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
#end
The problem is that this doesn't work on iOS7, the label in DetailViewController doesn't get updated. I try to set the label text before and after of pushViewController.
This works on all previous versions of iOS.
Why is not working on iOS 7 ??
The only way I get this working is, like I saw on this other question:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
// Create the next view controller.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
dispatch_async(dispatch_get_main_queue(), ^{
detailViewController.detailLabel.text = [_items objectAtIndex:indexPath.row];
});
}
Can somebody help me out to understand what is going on here??
Thanks!
_
EDIT
it also works like this:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Navigation logic may go here, for example:
// Create the next view controller.
DetailViewController *detailViewController = [[DetailViewController alloc] initWithNibName:#"DetailViewController" bundle:nil];
// Push the view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
if (detailViewController.view) {
// do notihing
}
detailViewController.detailLabel.text = [_items objectAtIndex:indexPath.row];
}

At the time when you set the value of the UILabel, the view has not yet been loaded, so if you check in debug, you'll probably find detailLabel is nil.
Why not pass the value in a custom init method? e.g.
- (id)initWithDetails:(NSString *)detailsText;
Then in viewDidLoad, assign the value?
According to your edit, in answer to your question as to why it is working:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
TestViewController *test = [[testViewController alloc] init];
NSLog(#"Before: %#", test.label);
if(test.view){}
NSLog(#"After: %#", test.label);
[[test label] setText:#"My Test"];
[self.navigationController pushViewController:test animated:YES];
}
I have mocked up your scenario, if you access 'view' on a ViewController, it will call viewDidLoad if the view does not exist. So your UILabel now becomes set. Here is the output from the console.
2013-10-11 16:21:04.555 test[87625:11303] Before: (null)
2013-10-11 16:21:04.559 test[87625:11303] After: <UILabel: 0x75736b0; frame = (148 157; 42 21); text = 'Label'; clipsToBounds = YES; opaque = NO; autoresize = RM+BM; userInteractionEnabled = NO; layer = <CALayer: 0x7573740>>
I recommend using a custom init method, rather than this approach - this above example is a hack. Use something like this E.g.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
testViewController *test = [[testViewController alloc] initWithText:#"My Test"];
[self.navigationController pushViewController:test animated:YES];
}
If you do not wish to use a custom initialiser, I suggest a public property on your DetailViewController of type NSString. Assign that using your approach after init is called. Then within DetailViewController, in viewDidLoad, or appear, set the IBOutlet to the value of the property containing the NSString.

Its not getting updated because you are making writing
#property (weak, nonatomic) IBOutlet UILabel *detailLabel;
You are making the UILabel as weak property. You must make it strong property.
#property (strong, nonatomic) IBOutlet UILabel *detailLabel;
As you are making your property weak so its getting destroyed.
To know more about weak and strong propertied you can refer
Differences between strong and weak in Objective-C

Related

UITableViewCells not displaying on screen after intercepting calls to UITableViewDelegate methods

While playing around with the iOS 7.1 SDK (on XCode 5.1.1), I am trying to intercept the calls to the delegate methods of UITableViewController by creating a class that implements the UITableViewDelegate and UITableViewDataSource protocol. I am expecting the tableView to delegate the calls to MyTableViewDelegate which does some customization and delegate back to HomeViewController.
Through debugging, I found tableView:cellForRowAtIndexPath: method and tableView:willDisplayCell:forRowAtIndexPath: of the HomeViewController, the drawRect: method of my custom UITableViewCell class was called, and the cells have right content. **So it seems to me that the cells are drawn to somewhere. But just not displaying on the screen (The table row divider lines were displayed on the screen though).**I wonder if anyone knows why it doesn't work. Below is the code snippet. Thanks in advance for your insights.
HomeViewController.h
// HomeViewController.h
#interface HomeViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource> {
UITableViewController *tableViewController;
UITableView *tableView;
}
#property(nonatomic, strong) MyTableViewDelegate *myDelegate;
#end
HomeViewController.m
// HomeViewController.m
- (void)loadView {
[super loadView];
tableViewController = [[UITableViewController alloc] initWithStyle:UITableViewStylePlain];
[self addChildViewController:tableViewController];
[tableViewController didMoveToParentViewController:self];
myDelegate = [[MyTableViewDelegate alloc] init];
[myDelegate setDelegate:self];
tableView = [[tableViewController tableView] retain];
[tableView setFrame:[[self view] bounds]];
[tableView setAutoresizingMask:UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth];
[tableView setDelegate:myDelegate];
[tableView setDataSource:myDelegate];
[tableView setClipsToBounds:NO];
[[self view] addSubview:tableView];
}
- (UITableViewCell *)tableView:(UITableView *)table cellForRowAtIndexPath:(NSIndexPath *)indexPath {
MyEntry *entry = [self entryAtIndexPath:indexPath];
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellClass"];
if (cell == nil) cell = [[[cellClass alloc] initWithReuseIdentifier:#"cellClass"] autorelease];
[self configureCell:cell forEntry:entry];
return cell;
}
MyTableViewDelegate.h
// MyTableViewDelegate.h
#interface MyTableViewDelegate : NSObject <UITableViewDelegate, UITableViewDataSource> {
}
#property (nonatomic, assign) id delegate;
#end
MyTableViewDelegate.m
// MyTableViewDelegate.m
#implementation MyTableViewDelegate
#synthesize delegate;
// Display customization
#pragma mark - Delegate
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if ([delegate respondsToSelector:#selector(tableView:willDisplayCell:forRowAtIndexPath:)]) {
<...some customization ....>
[delegate tableView:tableView willDisplayCell:cell forRowAtIndexPath:indexPath];
}
}
...
// and all other required and optional methods declared by the UITableViewDelegate and UITableViewDataSource protocol.
EDITED:
if I set the delegate and dataSource of tableView to the HomeViewController instance rather than MyTableViewDelegate instance, it works just fine.
// HomeViewController.m
- (void)loadView {
[super loadView];
...
[tableView setDelegate:self];
[tableView setDataSource:self];
...
EDITED:
Normally we can just do all the work in HomeViewController. However, in my case, I am trying to see if it's possible to insert a layer between HomeViewController and tableView. I have a special use case where I would expect HomeViewController to not be able to override the customization implemented in MyTableViewDelegate (intended to be a library). Hence, it's not a good idea to implement MyTableViewDelegate as a base class and make HomeViewController derive from it.
As far as I can see, the only connection between the HomeViewController and tableView is that tableView is managed as a subView by HomeViewController and tableViewController is also a childViewController of HomeViewController. Would the additional layer (ie. MyTableViewDelegate) break this connection since MyTableViewDelegate delegates every method call back to HomeViewController? If so, how does it break? Again, without the MyTableViewDelegate layer, the above code works just fine.

Xcode - didSelectRowAtIndexPath doesn't run

i have this simple code in the didSelectRowAtIndexPath method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath: (NSIndexPath *)indexPath {
NSString *selected = #"test";
NSLog(#"You choose: %#", selected);
}
This is my ViewController.h:
#import <UIKit/UIKit.h>
#interface ViewController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
IBOutlet UITableView *tableData;
}
#end
When i run the App, the TableView display all data, but then i cliked in a cell the method above doesn't run (?)
Have you set the delegate for the tableview in your storyboard, select your table view right click and drag to the view controller and you should see the option to set dataSource delegate and tableviewdelegate - I forget the exact names.
e.g.: thats part of my code, but should show you what you need to do. You need to hand over the data somewhere.
NSDictionary *oneDict = [_noteBookArray objectAtIndex:indexPath.row];
NSString *touchString = [oneDict objectForKey:#"value"];
NSString *dateString = [oneDict objectForKey:#"timestamp"];
//call the method who does it
[_noteBookViewController setContentAndTimeStampWith:touchString and:dateString];
//set the present View Controller active (the controller who contains the values)
[self presentViewController:_noteBookViewController animated:YES completion:nil];
method from the notebookviewcontroller
- (void)setContentAndTimeStampWith:(NSString *)contentString and:(NSString *)timeStampString
{_textInputView.text = contentString;
_timeStampString = timeStampString;}
got it?

pass data from selected row to view controller

I have a UITableView in a popover. When a user selects a row in the popover I am wanting it to close the popover and save some data in the cell to a variable in the parent view controller. What is the most efficient way to do this?
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
In this method you handle the user selection. For passing variables between view controllers from a UITableView to another ViewController read this great tutorial from ray wenderlich : http://www.raywenderlich.com/1797/how-to-create-a-simple-iphone-app-tutorial-part-1
Create a delegate in popover's table view controller and pass the variable to it as cell data
In .h of the popover's table view controller
#protocol PopoverTableViewControllerDelegate <NSObject>
- (void)didSelectRow:(NSString *)cellDataString;
#end
#interface PopoverTableViewController : UITableViewController
#property (strong, nonatomic) id<PopoverTableViewControllerDelegate> delegate;
#end
In the .m's didSelectRowAtIndexPath call the delegate and pass the cell data variable as
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[self.delegate didSelectRow:cellDataString];
}
- (void)dealloc
{
[super dealloc];
[_delegate release];
}
Implement it in parent view controller in .h implement the PopoverTableViewControllerDelegate as
#interface ParentViewController : UIViewController <PopoverTableViewControllerDelegate>
#property (strong, nonatomic) NSString *cellDataString;
#end
and in .m implement the delegate as
- (void)dealloc
{
[super dealloc];
[_cellDataString release];
}
PopoverTableViewController *popoverTableViewController = [[[PopoverTableViewController alloc] init] autorelease];
popoverTableViewController.delegate = self;
- (void)didSelectRow:(NSString *)cellDataString
{
self.cellDataString = cellDataString;
[popOverController dismissPopoverAnimated:YES];
}
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
ParentViewController *parent = [[ParentViewController alloc] initWithNibName:#"ParentViewController" bundle:nil];
parent.variable = //do something you want
[self.navigationcontroller pushViewController:parent animated:YES];
}
Something like this... Hope this helps...

How to copy UITableViewCell label to UILabel in nextView?

I just want to copy UITableViewCell's label (which is a simple string) into nextView's UIlabel.
I tried creating a string property in the nextView and passing it the cell label,
but it doesn't work.
I'm getting nil in nextView,
why is that? Here is my didSelectRowAtIndexPath Method
in rootViewController.m
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
NSString *selLabel =[tempArray objectAtIndex:indexPath.row];
DetailViewController *detailViewCont=[[DetailViewController alloc]initWithNibName:#"DetailViewController" bundle:nil];
detailViewCont.selectedLabel=selLabel;
[self.navigationController pushViewController:detailViewCont animated:YES];
NSLog(#"selected Label %#",detailViewCont.selectedLabel);
}
Last NSlog statement returns the correct string here.
In nextViewController.m
-(void)viewDidLoad
{
[super viewDidLoad];
selectedLabel=[[NSString alloc]init];
UILabel *label1=[[UILabel alloc]init];
label1.frame=CGRectMake(5,5,310, 60);
label1.font=[UIFont fontWithName:#"Arial Black" size:20];
label1.text=selectedLabel;
NSLog(#"sellabel %#",selectedLabel);
[self.View addSubview:label1];
}
NSLog statement here returns null
Delete the following line from you viewDidLoad method:
selectedLabel=[[NSString alloc]init];
This is clearing the value you set from the other view controller.
And this line:
label1.text=selectedLabel;
should really be:
label1.text = self.selectedLabel;
You setup a property, use it.
Try setting the selLabel before the pushViewController statement like below,
detailViewCont.selectedLabel=selLabel;
[self.navigationController pushViewController:detailViewCont animated:YES];
Move your line detailViewCont.selectedLabel=selLabel; before the pushViewController call. Your presenting the viewcontroller before you assign the value.
#interface DetailViewController
...
#property (strong, nonatomic) NSString *selectedLabel;
#end
#implementation DetailViewController
#synthesize selectedLabel = _selectedLabel; // define the instance variable associated with the property
-(void)viewDidLoad {
label1.text = _selectedLabel;
}
#end

Not possible to add row to a UITableView

I'm a newbie programming iOS and I've a problem adding a new cell to a UITableView object. I'm using an storyboard and one of the scenes is a UIViewController that has several subviews: textfields, a tableview, etc. I intend to add rows to this tableView from a detail scene.
I'm able to initially add rows to the table, but I'm not able to add a row afterwards. When I press a button to add the row I call the method '[self.stepsListTableView reloadData];' which produces a call to the method '- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section' and it returns a correct value, including the new array element. But method '- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath' is not called to update the table.
I do not understand what I'm doing wrong.
Details of my source code:
WorkoutDetailsViewController.h
(…)
#interface WorkoutDetailsViewController : UIViewController <StepDetailsViewControllerDelegate, UITextFieldDelegate, UITableViewDelegate, UITableViewDataSource>
#property (nonatomic, weak) id <WorkoutDetailsViewControllerDelegate> delegate;
#property (nonatomic, strong) Workout *workout;
(…)
#property (nonatomic, retain) IBOutlet UITableView *stepsListTableView;
(…)
WorkoutDetailsViewController.m
(…)
#synthesize stepsListTableView;
(…)
- (void)viewDidLoad
{
[super viewDidLoad];
addButton.enabled = FALSE;
workoutNameField.delegate = self;
if (self.workout == nil) {
self.workout = [[Workout alloc] init];
self.stepsListTableView = [[UITableView alloc] init];
}
self.stepsListTableView.delegate = self;
self.stepsListTableView.dataSource = self;
}
(…)
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
//return [self.workout.stepsList count];
NSInteger counter = 0;
counter = [self.workout.stepsList count];
return counter;
}
// Customize the appearance of table view cells.
- (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];
}
// Set up the cell...
// Pending
return cell;
}
- (void)stepDetailsViewControllerDidDone:(StepDetailsViewController *)controller
{
[self.workout.stepsList addObject:controller.step];
NSInteger counter = [self.workout.stepsList count];
[self.stepsListTableView beginUpdates];
NSArray *paths = [NSArray arrayWithObject:[NSIndexPath indexPathForRow:(counter-1) inSection:0]];
[self.stepsListTableView insertRowsAtIndexPaths:paths withRowAnimation:UITableViewRowAnimationTop];
[self.stepsListTableView endUpdates];
[self.stepsListTableView reloadData];
}
(…)
Also in the storyboard, I have setup the outlets delegate and dataSource to be the controller view.
Any idea ?
Regards,
JoanBa
I have solved the issue. I have discovered using debugger that method reloadData was called for a different UITableView than the one initialized in viewDidLoad.
I reviewed the UITableView settings in storyboard, which aparently were correct but I have deleted them and created again. Now it works.
In UIViewController header I have the line
#property (nonatomic, retain) IBOutlet UITableView *stepsListTableView;
and in implementation I have commented lines
//self.stepsListTableView.delegate = self;
//self.stepsListTableView.dataSource = self;
And, of course, in storyboard I have defined for the UITableView the following relationships:
Outlets: dataSource, delegate --> UIViewController
Referencing outlet: UITableView --> UIVIewController
That's it !!
You may have the table view set to static content. Select the UITableView in Storyboard and in the "Attributes Inspector" section of the menu on the right of screen select the "Content" field under the "Table View" header and set the value to "Dynamic Prototypes".
Screenshot for clarity:
This little trick caught me out several times when I was starting out.

Resources