TableView won't allow me to press up and transfer view. Obj-C, Cocoa Touch - objective-c

So I have an app that contains a tableView. When you press on a row, it should go to a new view controller and transfer the results. But it only sort-of works. If you press on a row, it will stay selected (Highlighted blue), but then if you select a different row, it will release the press on the first row and then move to the corresponding view. Sorry if this is a little confusing. Here is a short video of the error --> http://vid.ly/1e8u0h
It would seem as though the problem is that when I tap on a row, it never sends the "tap up" action, therefore doesn't continue with this method :
-(void)tableView:(UITableView *)tableView didDeselectRowAtIndexPath:(NSIndexPath *)indexPath
{
DetailViewController *detailViewController = [[DetailViewController alloc] init];
NSArray *items = [[BNRItemStore sharedStore] allItems];
BNRItem *selectedItem = [items objectAtIndex:[indexPath row]];
[detailViewController setItem:selectedItem];
[[self navigationController] pushViewController:detailViewController animated:YES];
}
How do I fix this? Thanks.

You're using didDeselectRowAtIndexPath instead of didSelectRowAtIndexPath :) Easy fix. Happy Coding.

You schould use tableview:didSelectRowAtIndexPath instead of tableView:didDeselectRowAtIndexPath.

Related

How do I detect the tap of a UIActionSheet button called inside a table view delegate method?

Ok I have a tableView with a UIActionSheet within it and there is no segue or anything a long those lines. A user has the option to swipe and delete a row or another option to take the user represented by a row a offline again.
When a user swipes they can delete the row.
When a user taps a UIActionsheet pops up with the option to "Take Offline" (destructive button) or cancel.
This is all wrapped in a tableView delegate method:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%d", [indexPath row]);
Person *person = [people objectAtIndex:indexPath.row];
UIActionSheet *popup = [[UIActionSheet alloc] initWithTitle:[NSString stringWithFormat:#"Need to edit %#'s info?", person.name]
delegate:self
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Take Offline"
otherButtonTitles:nil, nil];
[popup showInView:tableView];
[popup addButtonWithTitle:#"Cancel"];
[people removeObjectAtIndex:[indexPath row]];
[tableView reloadData];
}
I would normally use a UIActionSheetDelegate method to detect the tap of a button.
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex {
if (buttonIndex == 1) {
NSLog(#"Button 1 was clicked");
}
}
The problem I'm having is when I tap a row in my table the object is removed from the array and the table is reloaded instantly.
I don't want this to happen until the "Take Offline" button is clicked but can't figure out a way to do this. I've tried a future such as storing objects in instance variables and accessing them from other methods and trying to execute the code out of the tableView method but keep confusing myself.
What is the best way to do this?
Kind regards
I have written a blog post about how to (and why) add block callbacks to alert views, action sheets and animations:
http://blog.innovattic.com/uikitblocks/
Using this approach you don't have to use a delegate and can instead rewrite your tableView:didSelectRowAtIndexPath: method like so:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSLog(#"%d", [indexPath row]);
Person *person = [people objectAtIndex:indexPath.row];
UIActionSheet *popup = [[UIActionSheet alloc] initWithTitle:[NSString stringWithFormat:#"Need to edit %#'s info?", person.name]
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Take Offline"
otherButtonTitles:nil, nil];
[popup setHandler:^(UIActionSheet *sheet, NSInteger buttonIndex) {
[people removeObjectAtIndex:[indexPath row]];
[tableView reloadData];
} forButtonAtIndex:[popup destructiveButtonIndex]];
[popup showInView:tableView];
}
You can download the source files for this from GitHub:
https://github.com/Innovattic/UIKit-Blocks
Well, you're on the right line with the approach you say you've tried.
You'll need to keep a reference to the selected Person in a property. Declare a property like so:
#property (nonatomic, strong) Person *selectedPerson;
And store the selected person in this property in your didSelectRow... method. Then, use the UIActionSheetDelegate method to determine which button the user clicked. If they clicked 'Take Offline', then remove the person you stored in the selectedPerson property from the people array.

Update TableView (RootView) of SplitView

In my SplitView application I have some general data in the TableView at startup. When data in the TableView is selected, I want to display detailed data in the TableView at first and when something is selected there, it should be displayed in the DetailView, too. My question concerns the second step: How should I update the data for the TableView and avoid this TableView to disappear when something is selected?
If you are starting with Apple provided MasterDetail application, what it does by default is the following
Creates a split view controller.
On the left hand side it has a UINavigationController
On the right hand side it has DetailViewController
In the AppDelegate it has a UINavigationController with its rootcontroller as MasterViewController. (that is the reason you see MasterViewController) on the left.
The MasterViewController on the left does nothing related to navigation controller by default though. Looks like this is where you want to be able to drill down further and get another drill down view and so on.
Steps you need to take
First create another DrillDownLevel1ViewController which is a subclass of UITableViewController.
In that class, have two properties, one for data and one for reference to DetailViewController.
#property(strong,nonatomic)NSArray *drillDownLevel1Data;
#property(strong,nonatomic)DetailViewController *detailViewController;
In the MasterViewController's didSelectRowAtIndexPath do some thing like this
DrillDownLevel1ViewController *drillDownLevel1ViewController = [[DrillDownLevel1ViewController alloc] init];
drillDownLevel1ViewController.drillDownLevel1Data=[NSArray arrayWithObjects:#"One",#"Two",#"Three", nil];
drillDownLevel1ViewController.detailViewController=self.detailViewController;
[self.navigationController pushViewController:drillDownLevel1ViewController animated:YES];
In the DrillDownLevel1ViewController, you can correctly set numberOfSections, numberOfRows etc
In the cellForRowAtIndexPath of DrillDownLevel1ViewController, set correct value
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"Cell"];
if(cell==nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"Cell"];
}
cell.textLabel.text=[drillDownLevel1Data objectAtIndex:indexPath.row];
return cell;
In the didSelectRowAtIndexPath of DrillDownLevel1ViewController
self.detailViewController.detailItem = [drillDownLevel1Data objectAtIndex:indexPath.row];
[self.detailViewController configureView];
DetailViewController on the right hand side should show you the data now. If it is not, then you can do some debugging as to whether the outlets are connected etc.
I have a simple project which I built, if you want I can email it to you.

What are the possible reasons why -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath didn't get called?

I got a really strange problem.
My tableView has all the delegate and datasource set up.
Everything is fine.
However, clicking the rows do not activate:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
I used custom cells.
After I click and click and click and click and click, sometimes it goes through.
I wonder what can possibly cause that? It's as if the customCell is "absorbing" the touch event or something or what?
Could that be why? If so, if we want to implement customCell and we want the tableView to handle the touch up event, what should we do?
Additional symptom:
If I remove user interaction enabled from the custom cell then the problem is solved with a catch.
However, clicking the button will somehow erase all the label texts in the customCell.
The implementation of the custom Cell is the following:
- (BGUIBusinessCellForDisplay *) initWithBiz: (Business *) biz
{
if (self.biz == nil) //First time set up
{
self = [super init]; //If use dequeueReusableCellWithIdentifier then I shouldn't change the address self points to right
NSString * className = NSStringFromClass([self class]);
//PO (className);
[[NSBundle mainBundle] loadNibNamed:className owner:self options:nil];
self.frame =self.view.frame;
[self addSubview:self.view]; //What is this for? self.view is of type BGCRBusinessForDisplay2. That view should be self, not one of it's subview Things don't work without it though
}
if (biz==nil)
{
return self;
}
_biz = biz;
self.prominentLabel.text = [NSString stringWithFormat:#"Isi: %#", biz.isiString];
self.Title.text = biz.Title; //Let's set this one thing first
self.Address.text=biz.ShortenedAddress;
//if([self.distance isNotEmpty]){
self.DistanceLabel.text=[NSString stringWithFormat:#"%dm",[biz.Distance intValue]];
self.PinNumber.text =biz.StringPinLineAndNumber;
NSString * URLString=nil;
if(biz.Images.allObjects.count!=0){
//self.boolImage=[NSNumber numberWithBool:true];
Image * image=(biz.Images.allObjects)[0];
URLString = image.URL;
URLString = [NSString stringWithFormat:#"http://54.251.34.144/thumbnailer/Thumbnailer.ashx?imageURL=%#",URLString.UTF8Encode];
//url=[NSURL URLWithString:image.URL];
}else{
float latitude = biz.getCllLocation.coordinate.latitude;
float longitude = biz.getCllLocation.coordinate.longitude;
URLString = [NSString stringWithFormat:#"http://maps.googleapis.com/maps/api/staticmap?&zoom=16&size=160x160&maptype=roadmap&sensor=true&center=%f,%f&markers=size:small|color:blue|%f,%f",latitude,longitude,latitude,longitude];
URLString = URLString.UTF8Encode;
}
//Should add code and add loading indicator here
[BGHPTools doBackground:^{
UIImage * imageBiz = [BGMDImageCacherAndDownloader getImageFromURL:URLString];
[BGHPTools doForeGround:^{
self.Image.image=imageBiz;
[self.Image makeRound];
}];
}];
//self.view=self.view;
/*if (self.tableViewCell == Nil)//Instantiate that tableviewCell
{
PO(self.tableViewCell);
}
self.tableViewCell.business = bis;
self.pinLbl.text = bis.StringPinLineAndNumber;
self.lblTitle.text=bis.Title;
//self.pinLbl.text=bis.pinNumber;*/
//}
/*self.name=[dict objectForKey:#"Title"];
self.address=[dict objectForKey:#"Street"];
CLLocation * cll=[[CLLocation alloc]initWithLatitude:[[dict objectForKey:#"Latitude"] doubleValue] longitude:[[dict objectForKey:#"Longitude"] doubleValue]];
self.distance=[NSNumber numberWithDouble:[cll distanceFromLocation:[cachedProperties currentLocation]]];*/
return self;
Update: I already figure out why the texts are gone. Turns out my background is white. When a row got selected, the text suddenly turn into white. So by setting selected style to blue I sort of get that "fixed".
However, I still do not see where in my code I specify that all label texts should be white if the underlying tableViewCell is selected.
After all, what's selected is the cell, not the label. How the hell the label knows that it has to turn white is beyond me.
If you are using a Storyboard to handle the interface, instead of using:
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
Try using
#pragma mark --- Push selectedObject to the detailView ---
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
RRAppDelegate *myAppDelegate = [[UIApplication sharedApplication]delegate];
if ([[segue identifier] isEqualToString:#"PushObjectSegue"]) {
NSIndexPath *selectedRowIndex = [self.tableView indexPathForSelectedRow];
RRObjectViewController *detailViewController = [segue destinationViewController];
detailViewController.selectedObject = [myAppDelegate.goals objectAtIndex:selectedRowIndex.row];
}
}
I was having the same problem with the method you used and instead used this, it started working perfectly. Of course you'd have to adapt the code to your app's viewControllers and data source because I used my AppDelegate as the datasource, and I wasn't using a custom cell.
The most likely thing is that a view in your custom cell is absorbing the touch. Sometimes this is what you want, e.g. a button that does something, rather than selecting the entire cell. Assuming you don't want this, then just set those views' userInteractionEnabled property to NO.
--Additional code for custom NIB loading.
All you have to do is register the NIB in your viewDidLoad routine:
[tableView registerNib: [UINib nibWithNibName:#"yourCellNibName" bundle:nil] forCellReuseIdentifier:#"yourCellTypeID"]
and then in your cellForRowAtIndexPath just call:
newCell = [tableView dequeueReusableCellWithIdentifier #"yourCellTypeID"];
...
return newCell;
And it will load a cell from your XIB (or give you one from the previously used queue).
I just want to update that I think I have figured out what the problem is but still can't solve that quite right yet. And well the update is comprehensive so I think it should be an answer though I hope it's not the answer because some puzzle is still missing.
All the problem is interrelated.
The problem is in this line:
[self addSubview:self.view];
I basically turn that into:
Basically the my custom view cell has a view whose type is also tableViewCell. That view cover the real tableViewCell.
That's why when user interaction is enabled, that view will absorb the user's interaction.
That's also why the label "disappear". What happen is the label doesn't disappear. The label got highlighted and become white. However, what's highlighted is the tableViewCell not the opague view. The white opague self.view is still white while the tableCell itself is tinted with blue. So the label becomes white in the middle of white background and is gone.
I think I should replace [self addSubview:self.view] into self= self.view
However, that would mean changing the value of self. Yes it's in init. But it's still awkward. If anyone has the WAY to implement custom subclass of UI with XIB it'll be great because I haven't found one till now.
Awkward.
I wonder if we can draw a pointer to an XIB and specify that the outlet is self itself.
If that fail, I'll set background of self to white and background of self.view to transparent.
After tons of error and trying I did this:
self.contentView.backgroundColor = [UIColor whiteColor];
//self.view.backgroundColor = [UIColor redColor];
self.frame =self.view.frame;
/*PO(self.view.subviews);
PO(self.subviews);
PO(self.Title.superview);
PO(self.Title);
PO(self.view);
PO(self.Title.superview);
PO(self.view.contentView);*/
//Suck all the subviews from my minions
for (UIView* aSubView in self.view.contentView.subviews) {
[self.contentView addSubview: aSubView];
//[self.contentView add]
}
Basically I "move" all the subViews of my view object to my self object. There is a catch though that when subclassing tableViewCell I should move the subviews of the contentView. Who knows why.
At the end I just set self.view to nil for it's no longer needed and my program works as expected.
Also to set background of your tableViewCell, you need also to set the background of self.contentView rather than self.view.
Another approach you can try is to use story board. Alternatively you can just move the contentView of the self.view to self.
Make sure you'r implementing that method and not
deselectRowAtIndexPath:animated

tableView: didSelectRowAtIndexPath: inconsistency

I'm fairly new to Objective-C and iOS app development so I apologize if the answer to my question is simple. I'm trying to send a pointer of a GTGift object from one view controller to another. When I enter the following code, the object is send and stored in an instance variable of the second view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
GTGift *selectedGift = [[[GTGiftStore sharedStore] allGifts] objectAtIndex:[indexPath row]];
GTDetailViewController *dvc = [[self storyboard] instantiateViewControllerWithIdentifier:#"detailVC"];
[dvc setDetailGift:selectedGift];
[[self navigationController] pushViewController:dvc animated:YES];
}
However, when I enter this code in the first view controller:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
gift = [[[GTGiftStore sharedStore] allGifts] objectAtIndex:[indexPath row]];
GTDetailViewController *dvc = [[self storyboard] instantiateViewControllerWithIdentifier:#"detailVC"];
[[self navigationController] pushViewController:dvc animated:YES];
}
And this code in the second view controller:
- (void)viewWillAppear:(BOOL)animated {
GTGiftsViewController *gvc = [[self storyboard] instantiateViewControllerWithIdentifier:#"giftsVC"];
detailGift = [gvc gift];
NSLog(#"%#", detailGift);
}
detailGift returns null.
I can't for the life of me understand why, and it would be much more practical for my application if I could use an approach similar to the code segment that does not work.
If anyone can shine some light on this topic for me or point me in a different direction to complete the same task I would really appreciate it!
Your problem is that the word "instantiate" means what it says. :-)
In viewWillAppear you're creating a brand new GTGiftsViewController object which knows nothing about the gift that's been selected in the first GTGiftsViewController object.
(The "storyboard way" of doing this kind of thing is to create a prepareForSegue:sender: method in your first view controller and do the work there.)

Accessing UITableViewController > UIView > UIView?

Can anyone tell me if this is possible?
I have a setup a UITableViewController which consists of a number of cells, if you select one of these cells you are taken to a new viewController using pushViewController (see below)
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
NSUInteger row = [indexPath row];
NSLog(#"CELL__: You tapped the row ... %#", [list objectAtIndex:row]);
TestController *testController = [[TestController alloc] init];
[[self navigationController] pushViewController:testController animated:YES];
[testController release];
}
If in the implementation for "TestController" I would like to refer back to [self navigationController] (or maybe to add yet another view) is there any way to do that. My problem is that I need to access [self navigationController] to push further views and I don't know how to do that from the bottom controller?
Does that make sense?
Gary
From within TestController you reference the same UINavigationController via [self navigationController] as well.
EDIT to elaborate after comment:
The navigationController property is a property of UIViewController. If the controller is contained within a UINavigationController then it's own navigationController property will be a reference to that container UINavigationController. The same applies for tabBarController and splitViewController.