I'm wondering if I've somehow stumbled upon a bug where swipe to delete UITableViewCells don't ever deallocate when their UITableViewController does.
I've added a -dealloc method into my custom table cell, which logs out a message. When I press back on the navigation controller, all 6 messages are logged out.
If I swipe to delete one (or more), and press back, only 5 are logged. So I log out the pointer address of the cell that has been deleted, for future reference. Now when I go back and pause the execution at any point, I can enter into the lldb prompt:
po 140382950334240
<TableLeakCell: 0x7fe67852a490; baseClass = UITableViewCell; frame = (-375 176; 375 44); text = 'Chris'; hidden = YES; autoresize = W; gestureRecognizers = <NSArray: 0x7fe678530d70>; layer = <CALayer: 0x7fe67852a860>>
This should have been deallocated! The x.position of the cell is exactly -(tableView.width), and hidden=YES. As in the cell is still in the position after the animation has finished. Or perhaps it's being queued for reuse? Either way, it's there forever, and there's nothing in Apple's documentation about having to manually destroy a cell after calling deleteRowsAtIndexPaths.
Quick proof of concept project here:
https://github.com/iOSDigital/UITableViewLeak
At least using Xcode 7.2/iOS 9.2 Simulator and Xcode 7.3/iOS 9.3 Device all cells get deallocated. Only change is the order of deallocation, the deleted cell get's dealloc'd last.
Related
In my application I have a ViewController, In that view controller I have two CollectionViews - CollectionViewA and CollectionViewB in vertically format. And basically I have a design like calendar. In CollectionViewA - Displays the informations like, S.No, Image and favourite/unfavourite options. In CollectionViewB - Displays the program informations. So when I click the favourite option I'm reloading the collectionviews so that the collection view will get reload and update the informations in UI for favourite/unfavourite icon appearance.
So this is how the functionality works.
Now what happens is, this functionality works properly till tvOS 15.2. When we do the same functionality in 15.3 and above, there is an CollectionView scroll issue (i.e) when I click favourite for the item in row number 10 then after collectionview.reload() it automatically moves to the position from 10 to 5 or 3.
This automatic scroll issue is happening with the below error which is noted in log console.
Collection view focus state got out of sync. Expected (null) to be the
current managed subview but found <MyApp.CollectionViewCell:
0x114517ad0; baseClass = UICollectionViewCell; frame = (0 6532; 270
126); opaque = NO; tintColor = UIExtendedGrayColorSpace 0 0; layer =
<CALayer: 0x28339ab00>>.
Let me know if anyone can help me out from this issue!!!
When I transition between view controllers, the two latest ones I have created, in the debug, I am getting a large string that I'm not quite able to get to the bottom of.
Can anyone point me in the right direction/ help me to understand this and find the problem I've made?
I have compared all of my documents/view controllers to my working/non-error ones and can't find anything obvious.
2019-02-12 18:47:20.879463+1300 Techsupport[15324:9474292] <UIView:0x7ff96154a2f0; frame = (0 0; 375 812); autoresize = W+H; tintColor =UIExtendedSRGBColorSpace 1 0.149131 0 1; gestureRecognizers = <NSArray:0x600002c474e0>; layer = <CALayer: 0x6000021d42c0>>'s window is not equal` to <Techsupport.EslViewController: 0x7ff9618fd800>'s view's window!
It's easier to read if you take out all the details about the view:
Techsupport[15324:9474292] <UIView>'s window is not equal to <Techsupport.EslViewController: 0x7ff9618fd800>'s view's window!
It's hard to know exactly what's going on without seeing some of your code, but fundamentally the view that the error is complaining about (located at 0x7ff96154a2f0) belongs to a view hierarchy in a window that's different from the one that the view controller's view is in.
Here are some things to consider:
How are each of these views created?
Do you expect to have two windows? (Most iOS apps only have one.)
What are you trying to do with the view in question?
A good way to start is to set a breakpoint at the spot where the error occurs, and then work backward until you figure out where the view in question comes from.
As #Sachin Vas said, "it looks like you have a segue which is set to trigger automatically and also programmatically in the code." he was correct, I did have a mistake where I had called the function programmatically as well as automatically. Thanks for everyone's help!
This is probably the most frustrating situation I've dealt with, and it probably has the simplest solution.
I've got this UIButton storeB that gets properly initialized during the viewDidLoad: call of my UIViewController. Before I create the button, I create 5 other buttons.
During viewDidLoad:, I run this method called setupFBConditions which determines whether my forwardB button object should have an alpha of 0.5 with userInteractionEnabled set to 0. When this is the case, the button looks and performs the way it's supposed (essentially, like it's not there).
Furthermore, during this particular animation that is performed on one of my UIView objects, I decided to set the userInteractionEnabled property of my restartB object to 0 (so that no interaction occurs during the animation. That performance is also successful.
However, when I decide to write:
storeB.userInteractionEnabled = NO;
storeB.alpha = 0.5;
at the start of the animation (with the intention to keep this state till the game finishes or restartB is tapped again), and NSLog the button object, the log says that the userInteractionEnabled property is NO and alpha is 0.5, just like it should be. However, at no point do these properties get reflected to the UI.
storeB is my not even my last view to be added to the hierarchy (although it is the last UIButton). I have no idea why these changes are not taking effect.
I'm not going to post any more code because there's nothing to post besides the two lines above. It's that apparently simple of a problem + solution.
************ UPDATE **************
Here is the log report at the end of one of my swipe gesture actions (after storeB.userInteractionEnabled = NO;):
<UIButton: 0x1585ae80; frame = (270 8; 42 42); alpha = 0.5; opaque = NO;
userInteractionEnabled = NO; layer = <CALayer: 0x15899da0>>
<UIView: 0x1592bba0; frame = (0 0; 320 568); layer = <CALayer: 0x15934ae0>>
The frame is correct, the alpha and interaction settings are correct, and the superview frame (0,0,320,568) is exactly what it's supposed to be.
*********** SECOND UPDATE **************
In case anyone finds this useful...check the _retainCount property of your UIView/UIButton and see if it looks unusually high when taking into account all the operations done to the object. My retain count was 4 when it should have been 2, so every time I changed the property (of what I thought was my first instance of storeB), it was actually only affecting the second instance (that I didn't even know existed).
I'm trying to make a inputAccessoryView AND the keyboard modal for VoiceOver users so they don't get lost. Setting:
view.accessibilityViewIsModal = YES;
textField.inputAccessoryView = view;
Makes the view modal with keyboard visible but unusable - it is outside of the modal view.
Next I tried this after the keyboard has appeared:
textField.inputAccessoryView.superview.accessibilityViewIsModal = YES;
Which seems a bit out of bounds but almost worked - but the keys physical after B (N, M space, etc) were not navigable.
Next up was this:
textField.inputAccessoryView.superview.superview.accessibilityViewIsModal = YES;
and everything works just fine. That view, BTW logs as follows:
<UITextEffectsWindow: 0x17170290; frame = (0 0; 320 568); opaque = NO; gestureRecognizers = <NSArray: 0x17170710>; layer = <UIWindowLayer: 0x17170430>>
My question is this: I this a stupid thing to do? Seems like it. Is it legal? UITextEffectsWindow is private, but I'm not actually using it, just taking advantage of the fact that it's a view.
Any input or ways to work around the problem would help.
It's not illegal, but it is safe to assume it will stop working in future versions of iOS.
For example, in iOS 6, a developer could get from a cell to its tableview by calling self.superview. In iOS 7, there is now a private-classed view in between.
You may want to take a more dynamic approach, like traversing up the view hierarchy until you find a view with the correct frame, or with all of the views you need as its subviews. A similar approach for the UITableViewCell problem can be found here.
My app uses a UITableView with a UINavigationController to show a more detailed view when a row of the table is tapped - the basic drill-down routine.
When I tap on a row, it is highlighted, but the delegate methods tableView:willSelectRowAtIndexPath: or tableView:didSelectRowAtIndexPath: are not called (verified using debugger).
Now here's the weird part:
There are some other table views in the app (they don't drill down) and none of them exhibit the issue.
If I tap the row rapidly and repeatedly, after many tries (10 - 20 is normal), tableView:willSelectRowAtIndexPath: and tableView:didSelectRowAtIndexPath: are called and processing continues normally.
The problem occurs only on an (any, actually) iPad running iOS 6. It works fine with iPads running iOS 5, or with any iPhone running any iOS version, 6. It also works with the iPad simulator using iOS 5 or 6.
So it seems that something is receiving the tap before the delegate methods are called. But what?
I not using any UITapGestureRecognizer, so that is not the issue.
I am not using multiple UITableViewControllers for the table, so this is also not the issue.
I was having this issue with an app when running in iOS6. The tableView:didSelectRowAtIndexPath: method was never being triggered, even though it was working before updating to iOS6.
I finally figured it out by looking at the xib and the attribute inspector for the table. In the Table View section I had the Selection option set to No Selection and Show Selection on Touch was not selected.
Changing the Selection option to Single Selection actually got this working for me again and leaving the Show Selection on Touch unselected means that I don't see any flash or change of colour when the row is selected.
I encountered a similar issue. iOS 6 appears to be much more sensitive to the slightest upward movement on a tap on a table view cell. It appears it is registering the slightest movement as a scroll request rather than a cell selection. My table view was set to scrollEnabled = NO because it is a table view with static selection values. The table view appears within a small area of a larger iPad view. If I carefully tap a cell and lift directly up, the cell is selected.
To resolve it, I changed scrollEnabled to YES and made sure the area dedicated to my tableView was larger than the actual area needed to show the table view, thus preventing it from scrolling. Not sure if this is is the cause of the issue you are experiencing, but hope it helps.
I had the same problem. The UITableView was not triggering the didSelectRowAtIndexPath method when running in iOS6, but it was working fine in iOS5.1.
I had also implemented the
- (BOOL)tableView:(UITableView *)tableView shouldHighlightRowAtIndexPath:(NSIndexPath *)indexPath{
return NO;
}
and returned NO. In iOS5 it was all working fine, but the minute I switched to iOS6 it would cease to trigger the didSelectRowAtIndexPath. Once I removed that shouldHighlightRow method, everything worked in iOS6 again. Cheers!
The usual reason that didSeletRowAtIndexPath() doesn't get called is having a UITapGestureRecognizer on your viewcontroller with Cancels touches in view set to YES.
The correct and only sensible fix is to set Cancels touches in view to NO. Then your table row selection should work nicely.
Obviously this has some implications in your gesture handlers - you may need to add some code to your gesture handler to stop some touches going on to your views i.e.
- (IBAction)onTapGesture:(UITapGestureRecognizer *)sender {
if (sender.view == someOldViewThatINeedToDealWith) {
sender.cancelsTouchesInView = true;
}
}
I tried all the other answers posted, and although they seemed promising, they didn't solve the problem.
I've since discovered the cause of problem in my case: I was calling [self.tableView reloadData] on a different thread (due to handling an NSNotification event posted from a background worker thread).
I fixed the problem by changing [self.tableView reloadData] to
[self.tableView performSelectorOnMainThread:#selector(reloadData) withObject:nil waitUntilDone:NO];
Hope this helps.
The other suspect could be a Tap Gesture Recognizer set to the view controller's view swallowing all taps like a blackhole. Remove the gesture recognizer will enable did select. I just had the same issue and the tap gesture was the cause.
The resolution is really weird: the UITableViewController registered for all notifications. In the handler for the notifications, the table view data was being reloaded using
[self.tableView reloadData]
According to Apple DTS, when the table reloads data, this causes a notification to be sent to the observer, causing a race condition ...
No explanation for why it would work sometimes or why it would always work on an iPhone. But registering only for a small subset of notifications (i.e. the ones I was really interested in) fixed the problem!
Setting
recognizer.cancelsTouchesInView = NO;
takes care of it if your use case allows simultaneous handling
of touches by [tap] gesture recognizer (kdb dismiss for example)
and the view the recognizer is attached to.
in my case I hade button which received all touch events before tableviewcell