In my app i have a main UITableView, and every cell has its own functionality.
All the cells look like a disk case, with a disk inside, and when the user clicks on the cell, the disk animates itself out of the case, and disappears.
This animation is preformed by the UITableViewCell.
To make this animation possible, i had to make the disk view, a SubView of the UITableView superclass, so the animation can continue out of the cell.
-Till here everything works perfectly!
The problem:
Every time a UITableViewCell is shown, the UITableView calls the UITableViewCells method:
(void)refresh{
//Disk configuration:
[self.diskImage setHidden:NO];
[self addSubview:self.diskMainView];
[self.diskMainView setFrame:originalDiskFrame];
}
This method is returning the "diskMainView" to the original subview (self = UITableViewCell)
After this happens, the diskMainView View, is in the right place but it receives no user interactions,
So all the buttons that are on it do not call any methods.
I don't understand what is happening here.. i'm thinking maybe once you add UIViews and UIButtons form the cell, to itself, it cannot receive user interactions...
Any suggestions would help!
Thanks!!
Related
I am able to detect when a cell is removed from the UITableView by writing a handler for the method tableView:didEndDisplayingCell:forRowAtIndexPath:. This method is called whenever a cell is removed from the display. However, there is one exception.
When the cell has a UITextField and the field is the first responder, this method is never called even when it's scrolled off the display and the cells immediately before it and after it are.
The cell is also confirmed to be removed from the UITableView with a test while the cell is scrolled off the screen. The call to cellForRowAtIndexPath: returns nil under this condition.
I also subclassed the UITableViewCell and wrote a handler for removeFromSuperView. Again this method is called for all the cells when the are scrolled off the screen except when the cell has a UITextField and it is the first responder.
Another thing to note is that the UITextField in the cell accepts key input while it is scrolled off the screen and the call to cellForRowAtIndexPath: returns nil. I can see this when the cell is scrolled back into view.
Does anyone have any solutions for detecting when the cell is scrolled out of view, so that the controller can get access to the UITextField?
You could try to resign your first responder manually before the cell disappears. Depending on your requirements, this could be done in multiple ways, usually when the user starts scrolling. You could restore the first responder after he finishes scrolling, if the cell is still visible. Probably better from the graphical design point of view as well.
Alternatively, you could try to implement delegate's tableView:willDisplayCell:forRowAtIndexPath: and make a previous visible cells set, which you'd intersect with tableview's visibleCells array. The elements that are not in the current visibleCells but are in the previous were removed. Then assign a copy of visibleCells to previousVisibleCells.
This is probably a bug in Apple's code, you could file a radar for it.
I have a UICollectionView where I overrode hitTest:withEvent: in my UICollectionViewCells in order to allow for taps just outside of the cells to register as taps on the cells.
When I do this and I tap just outside the cells that now register as hits, I get calls to didHighlightItemAtIndexPath and didUnhighlightItemAtIndexPath, but I don't get a call to didSelectItemAtIndexPath. If I tap inside the cell I get all of the expected highlight and select item calls as I did before.
I don't have any custom gesture recognizers set up and I don't override touchesBegan or anything like that.
So does anyone know under what conditions you get a call for didHighlightItemAtIndexPath without a call to didSelectItemAtIndexPath? Is there any way to get my didSelectItemAtIndexPath called? Thanks.
EDIT
I forgot to mention that my UICollectionView is within a Today Widget, so it is contained within the Notification Center scroll view. If I move my select code into the didUnhighlightItemAtIndexPath, then it is called when you tap outside the cell, but the result is that you can't actually scroll the Notification Center without selecting one of the cells.
So perhaps the difference between the highlighting and selecting that I'm experiencing here has something to do with the scroll view responder canceling the selection outside of the cell?
OK, I figured out what was going on.
I added a new UITapGestureRecognizer to my UICollectionView. Implementing it like this led me the the solution:
- (void)cellSingleTap:(UITapGestureRecognizer *)sender
{
CGPoint point = [sender locationInView:collectionView_];
NSIndexPath *indexPath = [collectionView_ indexPathForItemAtPoint:point];
[ .... ]
}
When I checked the points returned when I got highlighting but no selection, it became apparent that it happened when the point tapped on was within the section insets of the collection view layout. And when the taps were on the section insets, the indexPathForItemAtPoint calls returned nil.
So basically the collection view will highlight, but not select, taps that are outside cells but are within its section insets. As long as the taps are outside the cells and not within the insets, those taps will result in calls to didSelectItemAtIndexPath.
Since I would like taps within the insets to count as taps on cells, I was able to workaround this issue by adjusting the tap points before my call to indexPathForItemAtPoint.
I have created EDITableViewCell, a subclass of UITableViewCell, so that I can put a UITextField inside of it, rather than a UILabel. I use EDITableView when overriding tableView:cellForRowAtIndexPath:. I populate a few cells with dummy data when initializing the app. The app seems to run fine. The UITableView is displayed as expected with three rows of the table populated with dummy data. The problem I am experiencing has do with scrolling the table.
If I pull up or down on the middle of any of the populated rows, there is no scrolling in response.
If I pull up or down on the middle of any of the unpopulated rows (something below the third row), there is scrolling in response, as would be expected with any UITableView. Because there are only three items, the table view bounces right back to where it was on touch up.
If I pull up or down on the edges of any of the populated rows (the portions of the cell that the UITextField doesn't extend to), there is scrolling in response as well.
Note: I have experienced this same (mis)behavior both with a programmatically-created subclassed UITableViewCell approach as well as with a prototype Storyboard approach.
It seems to me that the UITextField is in some way inhibiting the scrolling of my UITableView. Why would this be happening?
If this is inevitable, how would I go about creating a UITableViewCell subclass that does allow for in-place editing without inhibiting the scrolling?
Motivation: Like the edit event portion of Apple's Calendar app and many ToDo list apps, I want a UITableView full of cells that can be edited in-place, as opposed to segueing or using modals to edit elsewhere.
The issue here is having a UIResponder object (your UITextField) that is capturing the tap event and basically holding it so the UIScrollView (your table view) does not receive those events. A couple of suggestions for you to try:
Create a UITextField subclass that forwards touch events to the next responder.
Disable user interaction on the text field, and program when you want it to respond by calling becomeFirstResponder when needed.
For the first suggestion, your subclass would do something like this:
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self.nextResponder touchesBegan:touches withEvent:event];
}
But of course you would need to forward the other UIResponder methods as well.
I am a bit confused and don't have much else to turn to. I have an iOS application that gets messages from a server and populates it into a cell in a UITableView. Every time a certain object gets updated from the server that update triggers a cell animation. In our case we have a table of scores in a game. If someone loses a point their cell would flash red and if they gain a point their cell would flash green. Simple concept that works well and our users like the feedback. What they don't like is how when they scroll they see the cells have random colors associated with it and stay there for about a second. I believe that it has something to do with the way that the cells are being recycled. I tried various checks regarding this issue, checking that the cell has the same value, setting the color manually to white, etc. and non of these methods work.
All in all I am trying to only show the animation when the cell has been in view and not when the users is scrolling through the UITableView. Anyone have any advice or ideas?
Thanks
What is happening
Since table views reuse their cells you are ending up reused cells that is being flashed even though they shouldn't.
How to solve it
You can restore the reused cell to its default state either in preprareForReuse: on the custom table view cell or in the tableView:willDisplayCell:forRowAtIndexPath: delegate method (as ACB mentioned).
Since you are animating with UIView animations, you only need to change the value of the animated property to cancel the animation and return to the default state.
If you would have user Core Animation for the flash
If the animation was done using Core Animation you would instead have called removeAllAnimations on the animating layer. (For a flashing animation you probably wouldn't change the property on the layer, instead you would tell the animation to autoreverse. This would mean that removing the animation would change the color back to its original value.)
One option is to remove the animation and reset the color in willDisplayCell delegate method to the default color. If that doesn't work you can set the dequeueReusableCellWithIdentifier as follows,
NSString *aCellIdentifier = [NSString stringWithFormat:#"%d",indexPath.row];
UITableViewCell *aCell = [tableView dequeueReusableCellWithIdentifier:aCellIdentifier];
But this will be a problem if you have a lot data to display as it will create memory issues. Note that in this case you will not be reusing the table cells.
The purpose of this question is not to know when a UITableView is done loading its data (which has been answered in this post) but to know when a UITableView has done drawing all its cells.
Using the visibleCells property inside the
- (UITableViewCell *)tableView:(UITableView *)iTableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
method doesn't work because when the tableview is loaded for the first time, no cells are visible yet.
The idea is to animate the cell appearance when the tableview is refreshed.
This seems like a complex task to perform but I'm surprised Apple didn't provide any delegate method to use when a tableview is done loading.
Better try this method, pass NSArray of indexPaths that you want to reload with given set of animation
- (void)reloadRowsAtIndexPaths:(NSArray *)indexPaths withRowAnimation:(UITableViewRowAnimation)animation
Hope it works
I don't believe there is a method that indicates when a table has loaded it's cells. However, you can achieve your goal of animating in the table cell whenever it is drawn.
Logic:
UITableView displays UITableViewCells as needed.
Each UITableViewCell is a subclass of UIView.
UIView has drawRect, which is called every time the view is drawn.
So, put your animation in UITableViewCell's drawRect method & it'll animated whenever it's drawn.
Implementation:
Create a subclass of UITableViewCell,
Apply this subclass to your table cells,
Add the drawRect method, and
Implement your animation in the drawRect method.
I have done this in one of my projects and it worked great. It draws the animation when the table is loaded or scrolled and when the display is rotated. With this approach you don't need to know when the table is done loading – each cell is simply animated whenever it is drawn.
One final note – if you put another view controller on top of your table view and then dismiss it, you may need to reload the table and set each UITableViewCell to setNeedsDisplay. This will force drawRect to be called again for each UITableViewCell.