Ok so I have a custom animation being implemented inside willDisplayCell method. It is working fine when I scroll the view up and down. When I tap on one of the row, it will be pushed to another view controller to show more details and let user update the data.
The issue is when the user get back to the tableview. I called the [tableView reloadData] method inside the viewWillAppear to make sure updated data is shown. This will trigger the animation transition that I set up earlier.
My question is: Is there a way to only perform the animation when user scroll up/down the tableview, not when the reloadData is called?
If there's a way to mix between the willDisplayCell with scrollViewDidScroll or something along that line, it would be awesome.
Thanks!
The easiest solution would be to add a state flag that would tell the willDisplayCell whether it should actually animate.
Add a property to your UITableViewDelegate:
#property (nonatomic) BOOL shouldPreventDisplayCellAnimation;
Set the property before and after calling reloadData:
- (void)viewWillAppear:(BOOL)animated
{
…
self.shouldPreventDisplayCellAnimation = YES;
[self.tableView reloadData];
self.shouldPreventDisplayCellAnimation = NO:
}
Modify willDisplayCell to animate on condition
- (void)tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if (!self.shouldPreventDisplayCellAnimation) {
//animate
}
}
Related
I change the value of 2 UILabels in my "viewDidLoad" method, but I need the view to refresh after that in order to display the values. As it currently stands, the UILabels display the value of the previously selected cell. I need to do the refresh right after I change the labels' values. The "setNeedsDisplay" method is not doing the job.
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_nameLabel.text = _selectedLocation.name;
_addressLabel.text = _selectedLocation.address;
[self.view setNeedsDisplay];
}
Based on your comments, I think you are trying to do something like:
- (void)updateLabelTexts {
_nameLabel.text = _selectedLocation.name;
_addressLabel.text = _selectedLocation.address;
}
and wherever you are changing the _selectedLocation values:
//Just an example
-(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
UITableViewCell *cell = [tableView cellForRowAtIndexPath:indexPath];
_selectedLocation = _yourLocationsArray[indexPath.row];
//now you call your update method
[self updateLabelTexts];
}
The point is that you have to call [self updateLabelTexts]; just after you update the values.
A very stupid bug. Turns out when I made the segue to transition into the next view, I actually dragged it from a physical cell on to the destination controller. However, I should've simply connected the sending uiview controller to the destination viewcontroller with the segue, and then manually handled the transition. That fixed it, so there's no need to "refresh or reload" the UIView as I was trying to do.
What is the best way of preventing the user from selecting a cell inside a UITableView, but allowing my program to call selectRowAtIndexPath: on the table view?
I also want the controls in the UITableViewCell to remain interactive (i.e. allow touchesBegan: to be called on the UITableViewCell).
If I do [tableView setAllowsSelection:NO], calling selectRowAtIndexPath: does not do anything.
I realized that when you call selectRowAtIndexPath: programmatically, then the delegate method willSelectRowAtIndexPath: will not be called. However, if a user taps a cell, then this will be called. This method can return nil to prevent selection.
- (NSIndexPath *)tableView:(UITableView *)tableView willSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
return nil;
}
This will ensure that only programmatic calls to selectRowAtIndexPath: will select a row and any taps to select a row will not.
Uncheck the box : User Interaction Enabled in TableView as well as TableViewCell
I have the following UITableViewCell (well, subclassed).
With didSelectRowAtIndexPath it is possible to capture that a cell has been selected in UITableViewController. My problem occurs due to the fact that directly pressing Choose User bypasses the selection of the cell.
How could I allow my UITableViewController to be aware that UITableViewCell foo has been pressed even if the user immediately hits Choose User?
N.B. I don't need the Selection capability per se, this was just by method of knowing that a user had tapped within a cell area.
You could just call the method directly. If we say that for each Choose User button we are setting the row number as the tag and assuming that you don't have sections so everything will happen in section 0 we could do.
- (void)hitChooseUser:(id)sender
{
// Do whatever you want for when a user hits the `Choose User` button
// Code......
// Then do this at the end or whenever you want to do it.
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:[sender tag] inSection:0];
// Also assuming you have created you have created you UITableView correctly.
[self tableView:myTableView didSelectRowAtIndexPath:indexPath];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
// Do whatever it is you want.
}
I also found this link that may help you Manually call didSelectRowatIndexPath
You could also disable the user interaction with the cell itself by setting userInteractionEnabled: to NO for each cell in cellForRowAtIndexPath: so didSelectRowAtIndexPath: will only get called when you want to call it manually.
Do not call didSelectRowAtIndexPath: It is a UITableViewDelegate method and, where possible, should be used as such (meaning let the UITableView send messages to it). In addition, it creates an unnecessary dependency on UITableView implementation.
That being said, in order to achieve shared behavior that is performed either on button click, or on row selection, refactor it out into a common method that is not coupled with UITableViewDelegate
For example:
-(void)doSomethingCommon {
//do shared code here
}
-(void)chooseUserButtonPressed:(id)sender {
[self doSomethingCommon];
}
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
[self doSomethingCommon];
}
And if your UITableView shows more than one of these rows, for which you depend on knowing which corresponding model object is related to the cell, than you can use the tag property on UIView subclasses (usually something in your cell) to mark the row that the object is shown in.
I have a UITableView with a UITextField inside of each cell. A model object that stores the index of the cell that is currently being edited. If the cell scrolls off-screen, my app takes away first-responder status. (Failing to do so may cause problems). Now, suppose a cell (possibly the same one, or possibly a different one) corresponding to that index is about to scroll back onto the screen. I want to make that cell's textField the firstResponder. My delegate does receive a call
tableView: willDisplayCell: forRowAtIndexPath:
corresponding to the new cell. However, calling becomeFirstResponder: at that point does not help as the cell won't accept firstResponder status until it has been displayed.
Short of using a timer, any ideas for how to call becomeFirstResponder: at a point when the cell is in fact able to become the first responder?
EDIT: cellForRowAtIndexPath: is always called before willDisplayCell:. So no help there.
I haven't tried this, but the first thing I'd try is in cellForRowAtIndexPath...
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
// standard stuff to build cell and setup it's state
if ([indexPath isEqual:self.myModel.indexPathOfTextFieldBeingEdited]) {
// you probably have a handle to the text field from the setup above
UITextField *textField = (UITextField *)[cell viewWithTag:SOME_TAG];
[textField performSelector:#selector(becomeFirstResponder) withObject:nil afterDelay:0.0];
}
return cell;
}
You have to show cell on the screen to make it as first responder. Do at first:
[self.tableView scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionMiddle animated:NO];
and then call first responder on it's label/textField.
Here's what I did in MonoTouch - it's important that you do not animate the ScrollToRow() - i.e. "animated:NO" as shown in the answer by edzio27 (thanks edzio27 :) ).
var newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
if (newCell == null)
{
tableView.ScrollToRow(newIndexPath, UITableViewScrollPosition.Middle, false);
newCell = (UIOrderLineCell)tableView.CellAt(newIndexPath);
}
First, why: I have a class called AKTableViewController that inherits from UITableViewController and most of my View Controllers inherit from that.
I'm tryingto make a Category to add a banner on the bottom of each screen, not scrolling with the content, so I add my banner as the self.tableView.tableFooterView and than I position it on scrollViewDidScroll:.
My problem is, when the data is reloaded the banner is sent back to the right position and I don't have any callback to properly position it.
So: How to be notified when an UITableViewController has reloaded its data?
Use the delegate method for UITableView:
-(void) tableView:(UITableView *)tableView willDisplayCell:(UITableViewCell *)cell forRowAtIndexPath:(NSIndexPath *)indexPath
{
if([indexPath row] == ((NSIndexPath*)[[tableView indexPathsForVisibleRows] lastObject]).row){
//Finished loading visible part of your table
}
}
The way you're doing this seems convoluted. It sounds like you should be adding the banner as a separate view, not as a footer of the table view.