Changing tableview row height on the fly - objective-c

I have a table view with 2 rows. initially setting the row heights accordingly. i then load another view controller which displays a list from which the user can select multiple items. these items will be drawn on the first row of the tableview as custom images. when the user clicks back, the selelction of items the user has selected are displayed, but i need to be able to adjust the height of row 0.

Try this:
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath

on your TableListController header file declare:
#property (nonatomic, retain) NSIndexPath * expandedCellIndexPath;
then on the .m add
- (void)changeCellHeights:(NSIndexPath *)indexPath
{
if (self.expandedCellIndexPath) {
[self.yourTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject: self.expandedCellIndexPath] withRowAnimation:UITableViewRowAnimationFade];
}
self.expandedCellIndexPath = indexPath;
[self.yourTableView reloadRowsAtIndexPaths:[NSArray arrayWithObject: self.expandedCellIndexPath] withRowAnimation:UITableViewRowAnimationMiddle];
}
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath
{
DebugLog(#"");
if (self.expandedCellIndexPath && indexPath.row == self.expandedCellIndexPath.row && indexPath.section == self.expandedCellIndexPath.section) {
return 80;
}
return 56.0;
}
And finally don't forget to set your tableCell to Clip subviews (on your Xib, if you're using one)
All you need now is to call the changeCellHeights: method

Related

Min and Max button functionality in UITableVIew

I want to do min/max (expand/collapse) button functionality.Please see bellow image for more information.Is anybody know how to develop this functionality.I have some examples but those are not like exactly what i want.Please help me to solve this problem.
I've got a similar kinda requirement few days back. I have done this using custom tableview cell.
Firstly, you have to pass the indexpath of each row to the cell. For this i have taken a property in custom tableview cell.
i.e, #property(nonatomic,assign) NSIndexPath *indexPathOfCell;
Now, you have to pass the indexpath of row in datasource method.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath;
And you have to write a delegate method in custom tableviewcell so that when the user clicks on the min/max button you can reload that cell through this delegate call.
-(void)reloadCellWithIndexPath:(NSIndexPath*)indexPath;
You have to set the view controller as delegate for the custom tableview cell in which you are populating the table. In that class you have to maintain an NSMutableArray.
NSMutableArray *selectedIndexPaths;
When ever the reloadCellWithIndexPath gets called you can simply do same.
-(void)reloadCellWithIndexPath:(NSIndexPath*)indexPath{
if(![selectedIndexPaths containsObject:indexPath]){
[selectedIndexPaths addObject:indexPath];
}else
[selectedIndexPaths removeObject:indexPath];
[tableView reloadData];
}
In heightForRowAtIndexPath method you can change the height for a particular row.
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath{
BOOL cellModeChanged = NO;
for (NSIndexPath *indexpath in selectedIndexPaths) {
if(indexpath.section == indexPath.section && indexpath.row == indexPath.row)
cellModeChanged = YES;
}
if(cellModeChanged)
return 400.0;
else
return 100.0;
}

Retain Check Marks with Modal Segue

I am new to x-code and I was wondering: Is it possible to make check marks retain though you go through a modal segue?
I will check some boxes on my list:
But when I press done and then come back to the screen with a modal segue it appears as such:
Is it possible to make these checkmarks stay though I change views modally?
I have this code to create the checkmarks:
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
// Return the number of sections.
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
// Return the number of rows in the section.
return [self.toDoItems count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"ListPrototypeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
NewItem *toDoItem = [self.toDoItems objectAtIndex:indexPath.row];
cell.textLabel.text = toDoItem.itemName;
if (toDoItem.completed) {
cell.accessoryType = UITableViewCellAccessoryCheckmark;
} else {
cell.accessoryType = UITableViewCellAccessoryNone;
}
return cell;
}
#pragma mark - Table view delegate
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
[tableView deselectRowAtIndexPath:indexPath animated:NO];
NewItem *tappedItem = [self.toDoItems objectAtIndex:indexPath.row];
tappedItem.completed = !tappedItem.completed;
[tableView reloadRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationNone];
}
When you pop or dismiss a view controller, that view controller is gone. However, you have a few options to remember the state that view controller was in. The easiest way is probably to store a global variable, maybe an NSArray, to remember the checked items. Then when you load this view controller, you could "check" any items that exist within that NSArray.
Note that this method will only work for the lifetime that the app is open. If they closed the app, it would be gone. If you wanted to maintain the "checked" items for the next time they opened the app, you would need to store it in the NSUserDefaults - data in there is available until the app is deleted from the phone.

Cell text overlaps the editingAccessory when try to delete a row in iOS7

When I compile my app in the new iOS7, I found a problem when entering in the edit mode of an UITableView.
When I press in the red minus button to delete a row of the table, this row indent to the left to let appear the 'Delete' button. However, when this button appears, the text of the cell overlaps the editingAccesory (this happens only when the text is longer than the length of the cell).
How can I remove the overlapping?
Edit: Images in the comments
Edit 2: Tis is the code of the creation of the table
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [_tweetList count];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"SessionDetailCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier forIndexPath:indexPath];
Tweet *tweet = [_tweetList objectAtIndex:indexPath.row];
cell.textLabel.text = tweet.text;
return cell;
}
- (void)tableView:(UITableView *)tableView commitEditingStyle:(UITableViewCellEditingStyle)editingStyle forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (editingStyle == UITableViewCellEditingStyleDelete) {
[tableView beginUpdates];
Tweet *deletedTweet = [_tweetList objectAtIndex:indexPath.row];
[_selectedSession removeTweetsObject:deletedTweet];
[deletedTweet deleteEntity];
_tweetList = [Tweet findAllSortedBy:#"index" ascending:YES withPredicate:[NSPredicate predicateWithFormat:#"session == %#",_selectedSession]];
[tableView deleteRowsAtIndexPaths:#[indexPath] withRowAnimation:UITableViewRowAnimationFade];
[tableView endUpdates];
}
[[NSManagedObjectContext defaultContext]saveToPersistentStoreWithCompletion:nil];
}
- (void)tableView:(UITableView *)tableView accessoryButtonTappedForRowWithIndexPath:(NSIndexPath *)indexPath
{
selectedPath = indexPath;
[self performSegueWithIdentifier:#"EditTweet" sender:self];
}
Solution:
Finally, I put the accessoryButton in the default state, and I use the edit state only to delete the rows. It's the only solution I've found :(
Perhaps, the method "willTransitionToState" can help people to solve similar problems.
You can hide or remove editingAccesory in editing mode , so there is no overlapping there,
set this,
Screenshot:
I came across this question, because in iOS 8.3 I faced the same problem, that it seems not possible to correctly display the editing accessory AND the delete confirmation without the cell content and the accessory item to overlap. Solving this issue without breaking the transition-animations was quite a challenge. ;)
So here is my solution (assuming that you are using autolayout constraints in IB):
Create a UITableViewCell subclass and link it to your table view cell in IB.
Add an outlet from the autolayout constraint specifying the horizontal spacing bewteen the right-most view and the cell's content view (trailing to margin).
Override willTransitionToState and layoutSubviews as shown below.
Table cell subclass:
#IBOutlet weak var horizontalSpaceConstraint: NSLayoutConstraint!
override func willTransitionToState(state: UITableViewCellStateMask) {
if (state & UITableViewCellStateMask.ShowingDeleteConfirmationMask == UITableViewCellStateMask.ShowingDeleteConfirmationMask) {
self.horizontalSpaceConstraint.constant = 49.0; // ugly, delete-confirmation width
}
super.willTransitionToState(state)
}
override func layoutSubviews() {
if (!self.showingDeleteConfirmation) {
self.horizontalSpaceConstraint.constant = 0.0;
}
super.layoutSubviews()
}
The reason why I cannot use didTransitionToState() (and use layoutSubviewsinstead) to reset the layout constraint is, that this function is simply not invoked (as of iOS 8.3) after transitioning from the delete-confirmation-state. It seems Apple did only handle the case, that the user actually deletes the row, but not the case that the delete-confirmation is closed without deleting the row. :(

Return NSIndexPath from custom cell ? (UITableView)

I need to get the NSIndexPath for a custom cell in a UITableView. Here's the problem: I send a NSNotification from my custom cell to my UITableViewController when editingDidBegin gets called from a UITextField in my custom cell. In my UITableViewController, I resize the UITableView when the UITextField began editing, and then want the table view to scroll to the Cell in which the UITextField is first responder. But I can't figure out how to return the indexPath of the cell where the UITextField is being edited. I have tried so many ways, but its still not working. One way is this: in my cstomCell class i select the row using [self setSelected:YES] and in my TV controller then if I NSLog the row of [self.tableV indexPathForSelectedRow] it always returns 0 even though its always not 0.
Just give the cell a value for its tag property. Then you can get that cell by calling this
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. That way you will not have to setup a NSNotification in the Custom Cell. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
}
The above UITextField delegate method should get you the indexPath for the cell you have currently selected.
try this code..
- (void)textFieldDidBeginEditing:(UITextField *)textField
{
UITableView *tableView;
UITableViewCell* superViewCell = [self returnSuperCellViewOfTextField:textField];
if(superViewCell)[tableView scrollToRowAtIndexPath:[tableView indexPathForCell:superViewCell] atScrollPosition:UITableViewScrollPositionMiddle animated:YES];
}
-(id)returnSuperCellViewOfTextField:(id)viewToCheck
{
id superView = [viewToCheck superview];
if([viewToCheck isKindOfClass:[UITableViewCell class]] && superView)return superView;
else if(([viewToCheck respondsToSelector:#selector(superview)] && superView)) return [self returnSuperCellViewOfTextField:superView];
return nil;
}
Does this (your answer that you gave before below)still works if you have multiple texfields in each cell?:
UITableViewCell *cell = (UITableViewCell *)[self.tableView viewWithTag:tagValue];
then once you have the cell you can get the NSIndexPath like this
NSIndexPath *indexPath = [self.tableView indexPathForCell:nextResponderCell];
Since you have a UITextField in your custom cell you can place cell.textField.delegate = self; in the
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
data source method. Also in this same method you can tag both your cells text field like this cell.textField.tag = indexPath.row; and the cell like this cell.tag = indexPath.row;
Now that you have set the UITextField delegate, you can now place this method in your UITableViewController class
- (void)textFieldDidBeginEditing:(UITextField *)textField {
CustomCell *cell = (CustomCell *)[self.tableView viewWithTag:textField.tag];
NSIndexPath *indexPath = [self.tableView indexPathForCell:cell];
I have a tableview with 8 cells, and in each cell i have 6 texfields, and also the tableview is dynamic, so the user can insert more cells...but i´m having trouble in how to save the text the user enters in each texfield. Is it possible that with "cell.textField.tag = indexPath.row", the controller identify´s the texfield i´m in?
I like to tag my cells like so:
/* Create a tag based on the section and row of a cell(used to retrive the cell for the beginEditingNextCell method. */
-(NSInteger)tagForIndexPath:(NSIndexPath*)path {
return path.section * 1000 + path.row + 1;
}
The other way is to pass the beginEditing data using a custom delegate, and include a cell model, which you can then use to figure out your path depending on how you are storing data.
Check out my github project, it has the code to handle this case, as well as resizing for the keyboard and tabbing to the next editable cell: https://github.com/andrewzimmer906/XCell

Initialize custom UITableViewCell

I'm trying to load a single custom cell into a UITableView and it keeps throwing an error
UITableView dataSource must return a cell from tableView:cellForRowAtIndexPath:
I have no idea why. I have linked my table view cell to the UITableViewCell definition in my code, but it keeps giving me this error. Here is my code; any help would be greatly appreciated.
#import "RegisterDeviceViewController.h"
#implementation RegisterDeviceViewController
#synthesize checkString;
#synthesize cellRegistration;
// The designated initializer. Override if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
/*
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization.
}
return self;
}
*/
//Change UITableView Style to Grouped
- (id)initWithStyle:(UITableViewStyle)style {
// Override initWithStyle: if you create the controller programmatically and want to perform customization that is not appropriate for viewDidLoad.
style = UITableViewStyleGrouped;
if (self = [super initWithStyle:style]) {
}
return self;
}
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
self.title = #"Registration";
[super viewDidLoad];
}
// Customize the number of sections in the table view.
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
return 1;
}
// Customize the number of rows in the table view.
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 1) {
if (indexPath.row == 1) {
return cellRegistration;
}
}
return nil;
}
//Pass search type over to rootViewController section2
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
/*
<#DetailViewController#> *detailViewController = [[<#DetailViewController#> alloc] initWithNibName:#"<#Nib name#>" bundle:nil];
// ...
// Pass the selected object to the new view controller.
[self.navigationController pushViewController:detailViewController animated:YES];
[detailViewController release];
*/
}
/*
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations.
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
*/
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc. that aren't in use.
}
- (void)viewDidUnload {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
#end
Okay. That's not how UITableView works. When the table view needs to draw a cell (ie, a row); it invokes tableView:cellForRowAtIndexPath: on the object specified in the dataSource property. It's your job to return a UITableViewCell from that method. This is how Apple does it (and how you should do it):
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"AnIdentifierString"];
if (cell == nil)
{
cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:#"AnIdentifierString"] autorelease];
}
cell.textLabel.text = #"This text will appear in the cell";
return cell;
}
The number of times that method will be invoked depends on the number of sections in the table view and the number of rows in each section. The process works like this:
Table View invokes the delegate method numberOfSectionsInTableView: on its dataSource (it knows it implements that method because the dataSource must adhere to the UITableViewDataSource protocol).
If numberOfSectionsInTableView: returns a number greater than zero, the table view will invoke the delegate method tableView:numberOfRowsInSection: on the dataSource. So if numberOfSectionsInTableView: returns 2, tableView:numberOfRowsInSection: will be invoked twice.
If each invocation of tableView:numberOfRowsInSection: returns a number greater than zero, the table view will invoke the delegate method tableView:cellForRowAtIndexPath: on the dataSource' So if tableView:numberOfRowsInSection: returns 5, tableView:numberOfRowsInSection: will be invoked five times (once for each individual row).
Your opportunity to customise how that cell appears is after you've received a useable cell, but before it is returned (where 'This text will appear in the cell' appears above). You can do quite a lot here; you should see the Class Reference for UITableViewCell to see everything you can do (all I've done is set it to show 'This text...'). The lines above that are a way for iOS to reuse cells for performance considerations. If you, for example, wanted to show a certain string from an array of strings, you could do this (notice the use of the indexPath variable): cell.textLabel.text = [someArrayYouHave objectAtIndex:indexPath.row];.
You wrote:
it keeps throwing an error
'UITableView dataSource must return a
cell from
tableView:cellForRowAtIndexPath:' But
I have no idea why..
But your -tableView:cellForRowAtIndexPath: says, in part:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
//...
return nil;
}
After reading the error message and looking at the code, do you not see the problem?
You are returning only one section, only one row
the section count and row count starts from 0.
Thats y you are getting this kinda error
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
if (indexPath.section == 0) {
if (indexPath.row == 0) {
//this checking is no necessary, anyway if you want use like this
//ensure that cellRegistration is UITableViewCell
return cellRegistration;
}
}
return nil;
}
Also refer this post for loading custom cells.
New iOS7+ solution optimized for Smoother Scrolling
You already can see old solutions but as far as huge amount of Apps will continue only iOS7+ support here is a way more optimized and correct solution.
Cell initialization
To initialize cell just call dequeueReusableCellWithIdentifier and iOS7+ systems are enough smart to handle if cell == nil or not. If during dequeue cell is nil system will automatically make a cell for you.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:#"cellIdentifier" forIndexPath:indexPath];
return cell;
}
Cell configuration
Then do your entire cell configuration in willDisplayCell method. Just create one method in your class that configures cell and here you go with better performance!
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath {
[self configureCell:cell forRowAtIndexPath:indexPath];
}
- (void)configureCell:(UITableViewCell *)cell
forRowAtIndexPath:(NSIndexPath *)indexPath {
// Configure your cell
}