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.
Related
I have an array with charts that I would like to display in a table view. Since drawing the charts takes a few milli seconds, which would make scrolling choppy, I would like to remove the old chart from cell.contentView of a re-used cell and add a new subview with the correct chart when scrolling (see source code below). This works: the charts are correctly displayed.
However, when adding the subviews, the used memory increases and scrolling the first time down is choppy (scrolling is not choppy after all rows were displayed once).
It seems that the chart data, which is already stored in an instance variable (strong) is copied (not just a reference) into the UITableViewCell.
I would like to avoid this so that less memory is used.
Summary: How can I avoid it that my charts are being copied into a UITableViewCell when using addSubview. Instead, I would like to add just a reference to my data when using addSubview.
static NSString *CellIdentifier = #"chart";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell==nil)
{
cell = [[UITableViewCell alloc]
initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:CellIdentifier];
}
[cell.contentView.subviews makeObjectsPerformSelector:#selector(removeFromSuperview)];
VMKChartData *chartData = [shinobiCharts_ objectAtIndex:rowNumber];
ShinobiChart *shinobiChart = chartData.shinobiChart;
[cell.contentView addSubview:shinobiChart];
[self setSeparatorInsets:cell];
return cell;
Don't use a plain UITableViewCell and addSubview:, but create a custom table view subclass that has a property #property (nonatomic, weak) UIView *chartView, in tableView:cellForRowAtIndexPath: just assign it.
cell.chartView = shinobiChart;
in the setter for chartView, add it to the content view.
-(void)setChartView:(UIView *)chartView
{
[_chartView removeFromSuperview];
_chartView = chartView;
[self.contentView addSubView:chartView];
}
My (rather complicated) situation is as follows:
TestView is a subclass of UIScrollView which implements -drawRect:, but at some point inside -drawRect: it'll call a method, let's say -drawAnotherPartWithRect:context:. This method is implemented by subclasses of TestView to draw individually a certain part of the context.
There are two subclasses of TestView which implement -drawAnotherPartWithRect:context:, which currently do the same thing inside it: Subclass1 and Subclass2.
As of now, frame size is the only different between the two during initialization.
An instance of Subclass1 is used as a table view's section header, and it works perfectly, yet if Subclass2 is used as a subview of the cell's content view, it'll display, yet not scroll. Its initialization is as follows:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"PortoAppSubjectViewTableViewCell"];
if (cell == nil) {
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:#"PortoAppSubjectViewTableViewCell"] autorelease];
Subclass2 *contentView = [[[Subclass2 alloc] initWithFrame:CGRectMake(0.f, 0.f, [tableView bounds].size.width, 32.f)] autorelease];
[contentView setContentSize:CGSizeMake(tableView.bounds.size.width * 3, 32.f)];
[contentView setTag:55];
[[cell contentView] addSubview:contentView];
}
[(SubjectTableViewCellContentView *)[[cell contentView] viewWithTag:55] setContainer:[[[[$container subGradeContainers] objectAtIndex:[indexPath section]] subGradeContainers] objectAtIndex:[indexPath row]]];
return cell;
}
The interesting thing is, the horizontal scroll indicator shows up and shows me that it's scrolling finely, yet the text (drawn with CoreText) doesn't move left/right along with it. That works out-of-the-box with Subclass1. Additionally, if Subclass2 is used instead as the view class of the section header view, it'll work finely.
So, what's up with horizontal scroll views and table view cells? I've checked out other related questions on SO but haven't been able to find any solution.
I solved the issue by instead of drawing directly to a UIScrollView subclass, drawing to a UIView subclass added as a subview of UIScrollView.
I'd still like some follow-ups, therefore leaving the question unanswered – why did it work with the header view and not with the table view cell then?
I am developing my first app in objective-c. In first view controller I am able to generate UITableview with dynamic prototype cells containing accessory button. When I click on the accessory button, it will navigate to next view controller, which would also having dynamic UITableviewcells with (Custom button)Select as accessory button. But, when I click on the specific select button, corresponding cell contents should be updated in the cell(In first screen) for which accessory button is clicked. I am able to get the details from the second screen to first screen. But, my custom cell in the first screen is not updating?
Please help whether there is any pre-defined function, which I've not found in the reference doc.
Thankyou in advance..
There is a solution algoritm come from beginning ios 5 development.
1.Take the indexPath in prepareSegue
// prepare selection info
NSIndexPath *indexPath = [self.tableView indexPathForCell:sender];
2.Send that to second view controller
UIViewController *destination = segue.destinationViewController;
[destination setValue:self forKey:#"delegate"];
id object = [self.tasks objectAtIndex:indexPath.row];
NSDictionary *selection = [NSDictionary dictionaryWithObjectsAndKeys:indexPath,#"indexPath",object,#"object", nil];
[destination setValue:selection forKey:#"selection"];
3.The second view controller later will send back the indexPath to the first controller
// prepare selection info back
NSIndexPath *indexPath = [_selection objectForKey:#"indexPath"];
id object = _textView.text;
NSDictionary *editedSelection = [NSDictionary dictionaryWithObjectsAndKeys:indexPath, #"indexPath", object, #"object", nil];
[_delegate setValue:editedSelection forKey:#"editedSelection"];
4.So in the getter method of first view controller which used to take the indexPath back from the secomd view controller, you take back the table cell by indexPath and change its value
#pragma mark - Call back by destination view
-(void)setEditedSelection:(NSDictionary *)dict{
NSIndexPath *indexPath = [dict objectForKey:#"indexPath"];
UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath];
....
}
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¢er=%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
At the moment, I have an iOS app that starts out with a NavigationView (root view) and a table contained in the root view below the navigation bar. I am attempting to load another view (hoping to make it another table) from a .nib file when the first row in the original table is selected. How can I do this?
Any help or advice would be appreciated. Thank you very much.
You need to make use of the UITableViewDelegate method, tableView:didSelectRowAtIndexPath:
Ensure that your tableview's delegate is set to your view controller inside Interface Builder, or if you are not using interface builder..
[tableView setDelegate:self];
Your view controller will then adopt the UITableViewDelegate protocol and respond to tableView:didSelectRowAtIndexPath: when a row is selected.
Loading your new view when the first row is selected will be implemented like so..
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if(indexPath.section == 0){ // the first row
MyView *myView = [[MyView alloc] init];
[self.navigationController pushViewController:myView animated:YES];
[myView release];
}
}