I'm working on a UI component right now, and as it behaves similarly to UITableView, I'm heavily modeling the delegate and data source protocols after those of UITableView. However, I noticed one method that I don't quite understand- 'canMoveRowAtIndexPath'.
This essentially allows the delegate to specify whether it wants the given cell to be 'movable'. However, wouldn't dropping another movable cell into a higher index than the immovable cell (i.e. 'above' it in the table) cause it to indirectly move anyway? (since every cell below the moved one would be pushed down one row).
So basically, my question is what is the point of this method? Can anyone provide an example use-case for it? Because I'm debating whether I should bother including it in my component or not.
If anything, I would think perhaps a more useful delegate method would be something such as 'canMoveRowInSection', which would allow you to specify whether any rows in a given section can be moved. That would then allow you to disable reordering of a particular section, and moving other rows outside of that section would not affect the ordering of the rows inside it.
I know Apple engineers provided this method for a reason, I just can't see what that reason might be.
Thanks for any insight!
canMoveRowAtIndexPath tells the UITableView that if the table is in editing mode, the cells (or cell, if you choose specifically) can be moved up and down.
It's up to you the developer to handle the other side of that move.
For instance, say you have an array (NSMutableArray to be exact) of "A", "B", "C" and you want to rearrange that array to be "B", "C", "A". You need to make that change in the array based on the location of the cell being moved and save that array.
Example
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath {
return YES;
}
- (void)tableView:(UITableView *)tableView moveRowAtIndexPath:(NSIndexPath *)fromIndexPath toIndexPath:(NSIndexPath *)toIndexPath
{
id objectToMove = [[array objectAtIndex:fromIndexPath.row] retain];
[array removeObjectAtIndex:fromIndexPath.row];
[array insertObject:objectToMove atIndex:toIndexPath.row];
[objectToMove release];
}
Section Example
This example says that if the table section is 0, then no cells can move. Any other section (say you have 3), those cells in section 1 and 2 CAN move. You will still need to handle the array accordingly.
-(BOOL)tableView:(UITableView *)tableView canMoveRowAtIndexPath:(NSIndexPath *)indexPath
{
if ( indexPath.section == 0 )
return NO;
return YES;
}
Related
I'm trying to create a homework planner app that has two types of TableCellViews in a View Based NSTableView. One type is a narrow bar that just has a label of what subject the below homework is for, and the other type is a row to input homework items. (I'll include a screenshot below.)
My question is: when creating new rows in a TableView, how do you specify which type of row you'd like to create? I'm assuming it has something to do with identifiers, but I can't find any information on how to use them in this way.
This is basically how it would look:
You are on the right track with the identifiers. Here's how you use them.
First setup your NSTableView with your specific row types (as you've probably already done). In the screenshot below I made one row with a title and description and another with a few buttons.
Next, you need to setup the desired identifiers. Click the first row in Interface Builder and select the Identity Inspector. Pick a unique identifier for your first row. Do the same for the other(s).
Finally, in your implementation create a new row of a specific type using the following code:
TableViewController.m
#pragma mark - NSTableViewDelegate
- (NSView *)tableView:(NSTableView *)tableView
viewForTableColumn:(NSTableColumn *)tableColumn
row:(NSInteger)row {
NSTableCellView *cell;
if(someCondition == YES) {
cell = [self.tableView makeViewWithIdentifier:#"ButtonRow" owner:self];
} else {
cell = [self.tableView makeViewWithIdentifier:#"TitleDescriptionRow" owner:self];
}
return cell;
}
If you're looking for a more in depth tutorial, check out Cocoa Programming L51 - View-Based NSTableView (YouTube video, not by me).
I have a UITableView which shows file names. When the user taps on the cell, I download the tapped file. For this, I am showing an activity indicator at the left side of a selected cell. After the download ends, the activity indicator will hide. (Remember, the other content in cell wont change).
There is no rule here to click only one cell at a time. The user may tap any number of cells to initiate the download process. I just start the download process and will add it in the operation queue.
Problem: My problem is, consider the scenario where the user taps 3 cells. So three cells will show activity indicator to represent their download processes. If the user scrolls the table view and comes back to the same cells, the activity indicators was hidden. This is because, the tableview's cell creation method will called only for visible cells. So, how can I store the state of each cell's activity indicator?
You can store which file is downloading. And for each row create activity indicator. Something like this:
NSArray* filesArr;
- (UITableViewCell*)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString* identifier = #"identifier";
YoursTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if(!cell){
cell = [[YoursTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
//.....
if(filesArr[indexPath.row].downloading)
[cell.activity startAnimating];
else
[cell.activity stopAnimating];
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
if(filesArr[indexPath.row].downloading)
return;
[self startDownload:filesArr[indexPath.row]];
filesArr[indexPath.row].downloading = YES;
[tableView reloadData];
}
As you allude in your question, the issue is in part an artifact of cell reuse. When a cell is dequeued, you must update its activity state on the cell, either by creating a property on the file object for downloading status as Sk0prion suggested, or by some parallel structure.
I would just mention an alternative. If you have only a few cells, you could conceivably bypass cell reuse and store the cells in a dictionary. By avoiding cell reuse, the status in essence, is borne by the cell rather than the object it references. Memory pressure is obviously at issue. I've rarely found a case where this is the preferred solution.
I have an IOS application, which uses a UICollectionView for displaying a horizontal grid of cells. All i need, is a clever way of (lazy) loading data when the user reaches the end of my UICollectionView. The code i use now is :
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
GridCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:Cell forIndexPath:indexPath];
/**
TODO
- Find a good way to check if the user has reach the end of the grid
- This next piece of code is for demonstration purposes. A real check has to be
done yet!!!
**/
// releases is my datasource //
if(indexPath.row == (releases.count - 1)) {
[self getNextListData];
}
return cell; // <-- finally return the cell
}
The problem with this code is : if a user scrolls really fast to the end, he has to scroll back and forth to load more items. This is really not user friendly and i need a better way to load items when needed.
Does anyone have any suggestions?
Thanks in advance.
UICollectionView inherits from UIScrollView. UIScrollView has a delegate, UIScrollViewDelegate that has a bunch of methods related to scrolling. In one of these delegate methods (such as scrollViewDidBeginDecelerating:), you can check to see how far along the page you are, with the contentOffset and contentSize properties. If you're close to the end (defined by whatever % is the end for you), fetch more data.
An advantage to using a scrollview method is that it will not cause collectionView:cellForItemAtIndexPath: to be slower (by forcing it to run code for every collectionview unnecessarily).
This is basically the same technique that Apple suggests for lazy-loading of UITableView data's, applied slightly differently. They have some sample code for that as well (such as LazyTableImages)
When I ran into this problem with table views, I had to do some user testing. I found that if I started my load 10 cells before the end of the table, the new cell should be ready. Assuming that you are doing background loading of the data, that is.
if (indexPath.row >= [releases count] - 10)
[self getNextListData];
You would want to do some testing to see what number works for you.
In addition, I also had a loading indicator cell. In my number of rows in section callback, I always returned 1 extra.
return [releases count] + 1;
At the start of my cell for row callback I returned a special loading indicator cell.
if (indexPath.row == [releases count])
return [JLTActivityCell sharedInstance];
I'm not sure exactly how that advice will apply to collection views, but hopefully it gets you pointed in a good direction.
I'm new at developing with XCode and Objective-C and I hope you can help me.
The problem is, I have an UITableViewController with an UITableView (created with the InterfaceBuilder).
The cells under the section headers are expandable.
Now I want to dynamically create multiple UITableViews under the existing TableView.
The style will be the same like the existing TableView's style.
Could you tell me how it is possible to create these TableViews programmatically?
Thank you very much
Michael
From what you are saying try using a grouped table view. Check out this link for a quick overview, and go to the grouped table view section.
Edit found this example here:
Seems like it is what you are looking for. And a very cool idea also.
You'll have to just make your own custom header row and just put that as the first row of each section. Subclassing the UITableView or the headers that are on there now would probably be a huge pain and I'm not sure you can easily get actions out of them the way they work now. You could easily set up a cell to LOOK like a header, and setup the tableView:didSelectRowAtIndexPath to expand or collapse the section it is within manually.
If I were you I'd store an array of booleans corresponding the the "expended" value of each of your sections. You could then have the tableView:didSelectRowAtIndexPath on each of your custom header rows toggle this value and then reload that specific section.
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.row == 0) {
///it's the first row of any section so it would be your custom section header
///put in your code to toggle your boolean value here
mybooleans[indexPath.section] = !mybooleans[indexPath.section];
///reload this section
[self.tableView reloadSections:[NSIndexSet indexSetWithIndex:indexPath.section] withRowAnimation:UITableViewRowAnimationFade];
}
}
You'd then setup your number numberOfRowsInSection to check the mybooleans value and return either 1 if the section isn't expanded, or 1+ the number of items in the section if it is expanded.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
if (mybooleans[section]) {
///we want the number of people plus the header cell
return [self numberOfPeopleInGroup:section] + 1;
} else {
///we just want the header cell
return 1;
}
}
You would also have to update your cellForRowAtIndexPath to return a custom header cell for the first row in any section.
Whats design pattern does UITableView use to populate and what are the benefits?
Is it delegate pattern? Reason I am asking is that it's not just delegate but the datasource as well.Seems more like along the line with MVC.
I have just gone through a couple of tutorials online their\my code is working but it looks like I am missing the point.I end with all these methods in my main controller.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation
{
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return 10;//any number based on datasource size.
}
// 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] initWithFrame:CGRectZero reuseIdentifier:CellIdentifier] autorelease];
}
// Set up the cell...
cell.text = [names objectAtIndex:indexPath.row];//names is an array.
return cell;
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
}
It is a view based application . Should it be in a sperate controller.Otherwise it just looks messy and over the top way of doing something simple. I am not at all saying Objective C or apple is wrong but just that I am a beginner and missing the whole point of this delegate and datasource setup.
so to summarise can someone please explain:
1-Whats the benefit of this delegate and datasource setup?
2-Whats the name of this design pattern?
3-Should I have a separate controller (in view based application)?
In Apple's parlance, delegate implements call-back methods which modify the UI behavior, and dataSource provides the data. In a bigger app, you can use two different objects to be the delegate and the data source separately.
I'm not familiar with the official terminology, sorry ...
Depends on the size of your app. Even if you just use appDelegate for everything, it's recommended to add
#pragma mark -- table view delegate methods
...methods...
#pragma mark -- table view data source methods
...more methods...
so that the method list is shown nicely inside Xcode.
Look into n-tier design methodologies.
Most all patterns are to create code thats adaptable to change (and less prone to bugs).
The benefit here is that with those pieces abstracted from your view controller will allow more flexibility and ideally less headaches for maintaining the code. If your requirements change, or your data you have to modify this file. Whereas with the data source isolated you could end up just having to modify that one file. More important with the data since it tends to be external and perhaps created by some other entity this is often changed. Also storage strategy may change, you could go from XML to core data with no impact on the view controller, delegate or views.
On the delegate, what if design changed and this same data was reused elsewhere. If the delegate isn't tied to this view controller this becomes a simple matter of reusing the delegate code where needed.
Then add other complicating factors, maybe you've subclassed UITableView. Perhaps, you display the same data in two different places, and in both cases you've subclassed UITableViewCell. Maybe the requirements aren't for two different views, but this view controller here has an option to display a detailed version and a brief version.
Food for thought.