Bug in iOS with UITableViewCells? - ios

I have a UITableViewController in a Storyboard in my iOS project. I have a special TableViewCell MessageCell with a UIView inside of a specific class.
When I call tableView.dequeueReusableCellWithIdentifier: I get a UITableViewCell with only this subview tree:
<UITableViewCell: 0x16da5e90; frame = (0 0; 0 0); layer = <CALayer: 0x16da5120>>
<UITableViewCellContentView: 0x16da6210; frame = (0 0; 0 0); clipsToBounds = YES; opaque = NO; gestureRecognizers = <NSArray: 0x16da5510>; layer = <CALayer: 0x16da6280>>
<_UITableViewCellSeparatorView: 0x16da62b0; frame = (0 -1; 15 1); layer = <CALayer: 0x16da52d0>>
But when I call tableView.dequeueReusableCellWithIdentifier:forIndexPath, I get this subview tree:
<UITableViewCell: 0x146f3a90; frame = (0 148.93; 320 54.93); autoresize = W; layer = <CALayer: 0x146f2ff0>>
<UITableViewCellContentView: 0x146f3020; frame = (0 0; 320 54.43); opaque = NO; gestureRecognizers = <NSArray: 0x146f3e90>; layer = <CALayer: 0x146f3180>>
<Heaven_Help.MessageView: 0x146f3f70; frame = (0 0; 320 568); autoresize = W+H; tag = 99; layer = <CALayer: 0x146f4af0>>
<UIImageView: 0x146f4010; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <CALayer: 0x146f4090>>
<UILabel: 0x146f40c0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x146f4180>>
<UILabel: 0x146f42d0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x146f4390>>
<_UITableViewCellSeparatorView: 0x146f31b0; frame = (0 -1; 15 1); layer = <CALayer: 0x146f3c20>>
<_UITableViewCellSeparatorView: 0x146f5170; frame = (0 -0.5; 16 0.5); layer = <CALayer: 0x146c1f40>>
The UIImageview and 2 UILabels get added in my MessageView initializer.
Who can tell me what I am not getting?
As requested: my code from tableView:cellForRowAtIndexPath::
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("MessageCell", forIndexPath: indexPath) as UITableViewCell
(cell.viewWithTag(99) as MessageView).setMessage(conversation[indexPath.row])
return cell
}

In short, don't use dequeueReusableCellWithIdentifier:.
dequeueReusableCellWithIdentifier: returns:
A UITableViewCell object with the associated identifier or nil if no such object exists in the reusable-cell queue.
This method is typically used in a pattern like this without a storyboard:
MessageCell *cell = [tableView dequeueReusableCellWithIdentifier:#"messageCell"];
if (!cell) {
cell = [[MessageCell alloc] initWithStyle:…reuseIdentifier:#"messageCell"];
// set properties that are true for EVERY cell
}
// set properties from your data model for THIS cell (usually strings and images)
return cell;
(With a storyboard, the method will always return a cell.)
The newer methods (like dequeueReusableCellWithIdentifier:forIndexPath:) are guaranteed to return a cell, so you don't need to use this pattern any more.
The reason you're getting a UITableViewCell back from dequeueReusableCellWithIdentifier: is because you inserted one into the reusable-cell queue with that identifier.

Related

Swift - UIViews order programmatically

I have several UIView layers in one ViewController.
How can I determine the order of UIView programmatically?
I found the solution...
In the UIView documentation, where are several methods listed for the manipulation of the order of subviews:
bringSubviewToFront(_:)
sendSubviewToBack(_:)
removeFromSuperview()
insertSubview(_:atIndex:)
insertSubview(_:aboveSubview:)
insertSubview(_:belowSubview:)
exchangeSubviewAtIndex(_:withSubviewAtIndex:)
In Swift 4.0
view.sendSubview(toBack:yourUIView)
view.bringSubview(toFront:yourUIView)
Views are ordered from back-most to front-most in view.subviews array.
For example, here are 3 subview added to an empty view.
view.addSubview(UILabel())
view.addSubview(UIButton())
view.addSubview(UITextField())
Printing the subviews with the following code
for view in view.subviews {
print(view.debugDescription)
}
has the output
<UILabel: 0x7feef00042e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400008c210>>
<UIButton: 0x7feef00045c0; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x604000222300>>
<UITextField: 0x7feeef02c800; frame = (0 0; 0 0); text = ''; opaque = NO; gestureRecognizers = <NSArray: 0x60800005b090>; layer = <CALayer: 0x604000222280>>
When the views are reordered, the position in view.subviews changes
if let button = view.subviews.first(where: { $0 is UIButton }) {
view.bringSubview(toFront: button)
}
Now, printing the view, has the output
<UILabel: 0x7feef00042e0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x60400008c210>>
<UITextField: 0x7feeef02c800; frame = (0 0; 0 0); text = ''; opaque = NO; gestureRecognizers = <NSArray: 0x60800005b090>; layer = <CALayer: 0x604000222280>>
<UIButton: 0x7feef00045c0; frame = (0 0; 0 0); opaque = NO; layer = <CALayer: 0x604000222300>>

tableHeaderView - layoutSubviews and initWithFrame

I have a custom UITableViewHeaderFooterView class and an xib for it. There is a label in this xib which is connected to the custom class using an outlet. This custom class is being used as the tableHeaderView for a table view in a view controller.
I am able to show the header view with required dimensions but the label in the header is not showing any value.
In view controller's viewDidLoad.
- (void)viewDidLoad {
..
DashboardHeaderView* headerView = [[DashboardHeaderView alloc] initWithFrame:CGRectMake(0, 0, 320, 180) headerText:#"XYX"];
self.alertsTableView.tableHeaderView = headerView;
}
In DashboardHeaderView which is a subclass of UITableViewHeaderFooterView.
- (id)initWithFrame:(CGRect)frame headerText:(NSString *)headerText {
if (self = [super initWithFrame:frame]) {
self.headerText = headerText;
NSArray* objects = [[NSBundle mainBundle] loadNibNamed:#"DashboardHeaderView"
owner:self
options:nil];
UIView *nibView = [objects firstObject];
UIView *contentView = self.contentView;
contentView.backgroundColor = [UIColor clearColor];
CGSize contentViewSize = contentView.frame.size;
nibView.frame = CGRectMake(0, 0, contentViewSize.width, contentViewSize.height);
[contentView addSubview:nibView];
}
return self;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.headerLabel.text = self.headerText;
}
If I put some log messages in initWithFrame:headerText: and layoutSubviews, I can see that the self of both is pointing to different addresses and hence in layoutSubviews, self.headerText is always nil.
layoutSubviews
<DashboardHeaderView: 0x7ff862cf1a90; baseClass = UITableViewHeaderFooterView; frame = (0 0; 320 180); text = ''; opaque = NO; autoresize = W+H; layer = <CALayer: 0x7ff861634e30>>
initWithFrame
<DashboardHeaderView: 0x7ff862cf05e0; baseClass = UITableViewHeaderFooterView; frame = (0 0; 320 180); text = ''; layer = <CALayer: 0x7ff8616d13f0>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf1a90; baseClass = UITableViewHeaderFooterView; frame = (0 0; 290 180); text = ''; opaque = NO; autoresize = W+H; layer = <CALayer: 0x7ff861634e30>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf05e0; baseClass = UITableViewHeaderFooterView; frame = (0 0; 290 180); text = ''; layer = <CALayer: 0x7ff8616d13f0>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf05e0; baseClass = UITableViewHeaderFooterView; frame = (0 0; 290 180); text = ''; layer = <CALayer: 0x7ff8616d13f0>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf1a90; baseClass = UITableViewHeaderFooterView; frame = (0 0; 345 180); text = ''; opaque = NO; autoresize = W+H; layer = <CALayer: 0x7ff861634e30>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf05e0; baseClass = UITableViewHeaderFooterView; frame = (0 0; 345 180); text = ''; layer = <CALayer: 0x7ff8616d13f0>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf1a90; baseClass = UITableViewHeaderFooterView; frame = (0 0; 345 180); text = ''; opaque = NO; autoresize = W+H; layer = <CALayer: 0x7ff861634e30>>
layoutSubviews
<DashboardHeaderView: 0x7ff862cf05e0; baseClass = UITableViewHeaderFooterView; frame = (0 0; 345 180); text = ''; layer = <CALayer: 0x7ff8616d13f0>>
If you look at above logs, why do we have two different addresses - 0x7ff862cf1a90 and 0x7ff862cf05e0.
UIView Class Reference says:
layoutSubviews - Implement this method if you need more precise
control over the layout of your subviews than either the constraint or
autoresizing behaviors provide.
You are setting a value of an UILabel inside the layoutSubviews method, that is intended only for resizing operations.
I suggest you to move the self.headerLabel.text = self.headerText instruction into the initWithFrame:headerText: method and perform other operations like resizing or changing constraints into the layoutSubviews method.
In this way it works fine!

When is UIView.didAddSubview called?

When is UIView.didAddSubview called ?
I have created a simple View and ViewController app.
When I run this app, didAddSubview is called twice on first time the View appears.
https://github.com/toshi0383/HelloUIView
below is the log output
didAddSubView
<_UILayoutGuide: 0x7f92d3425210; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7f92d3419cf0>>
didAddSubView
<_UILayoutGuide: 0x7f92d3425c10; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7f92d34244a0>>
viewDidLoad
[<_UILayoutGuide: 0x7f92d3425210; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7f92d3419cf0>>, <_UILayoutGuide: 0x7f92d3425c10; frame = (0 0; 0 0); hidden = YES; layer = <CALayer: 0x7f92d34244a0>>]
didMoveToWindow
didMoveToSuperView
layoutSubviews
<ViewTest.View: 0x7f92d3424bc0; frame = (0 0; 375 667); autoresize = W+H; layer = <CALayer: 0x7f92d3423320>>
UILayoutGuide is a private Apple class.
UILayoutGuide is normally referred to -topLayoutGuide and -bottonLayoutGuide, those are not really constraints.
same questions here:
stack overflow : what-is-uilayoutguide

indexPathForCell unrecognized selector sent to instance on iOS 8 with ObjC

I have a UITextField in UITableViewCell. When user tries to enter some text into this textfield, app scrolls that cell to the top of UITableView.Here is the code:
#pragma mark - UITextField Delegates
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
UITableViewCell *QCELL = (UITableViewCell *)[[[[textField superview]superview]superview]superview];
UITableView *QTable = (UITableView *)[[QCELL superview] superview];
NSIndexPath*path = = [QTable indexPathForCell:QCELL];
return true;
}
and exception
2014-09-11 11:12:50.604 InspectTHIS[9401:348011] -[UIView indexPathForCell:]: unrecognized selector sent to instance 0x7c2e8b30
2014-09-11 11:12:50.608 InspectTHIS[9401:348011] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[UIView indexPathForCell:]: unrecognized selector sent to instance 0x7c2e8b30'
Updated - Answered
After I search in Views Layer I found this
////////// iOS 8 //////////
<UIView: 0x792aabd0; frame = (0 0; 768 1023); opaque = NO; autoresize = W+H; layer = <CALayer: 0x792aac40>>
<UITableViewCellContentView: 0x79758340; frame = (0 0; 768 699); opaque = NO; gestureRecognizers = <NSArray: 0x79758530>; layer = <CALayer: 0x797583b0>>
<QuestionCell: 0x79757ed0; baseClass = UITableViewCell; frame = (0 0; 768 699); autoresize = W; layer = <CALayer: 0x79758190>>
<UITableViewWrapperView: 0x7967bc50; frame = (0 0; 768 750); gestureRecognizers = <NSArray: 0x7967bef0>; layer = <CALayer: 0x7967be30>; contentOffset: {0, 0}; contentSize: {768, 750}>
<UITableView: 0x78c99600; frame = (0 244; 768 750); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0x7967b9c0>; layer = <CALayer: 0x7967ae00>; contentOffset: {0, 0}; contentSize: {768, 699}>
<UIView: 0x7967abd0; frame = (0 0; 768 1024); autoresize = RM+BM; layer = <CALayer: 0x7967ac40>>
//////// iOS 7 ////
<UIView: 0x11fbbbf0; frame = (0 0; 768 1023); opaque = NO; autoresize = W+H; layer = <CALayer: 0x11fbbc50>>
<UITableViewCellContentView: 0x11f832a0; frame = (0 0; 768 699); opaque = NO; gestureRecognizers = <NSArray: 0x11f8dd40>; layer = <CALayer: 0x11faa6f0>>
<UITableViewCellScrollView: 0x11fc4090; frame = (0 0; 768 699); autoresize = W+H; gestureRecognizers = <NSArray: 0x11f9c830>; layer = <CALayer: 0x11f978f0>; contentOffset: {0, 0}>
<QuestionCell: 0x11fa2090; baseClass = UITableViewCell; frame = (0 0; 768 699); autoresize = W; layer = <CALayer: 0x11fa1c90>>
<UITableViewWrapperView: 0xced1860; frame = (0 0; 768 750); autoresize = W+H; layer = <CALayer: 0xced18d0>>
<UITableView: 0xd958800; frame = (0 244; 768 750); clipsToBounds = YES; autoresize = RM+BM; gestureRecognizers = <NSArray: 0xced1560>; layer = <CALayer: 0xced0eb0>; contentOffset: {0, 0}>
<UIView: 0xced0c30; frame = (0 0; 768 1024); autoresize = RM+BM; layer = <CALayer: 0xced0c90>>
as you can see in iOS 8, UITableViewCellScrollView is missing and and I can't go up like I did in iOS 7. so i change the code like this.
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField {
NSIndexPath*path;
UITableView *QTable;
CGFloat version =[[[UIDevice currentDevice] systemVersion] floatValue];
if (version >= 7.0)
{
UITableViewCell *QCELL = (UITableViewCell *)[[[[textField superview]superview]superview]superview];
QTable = (UITableView *)[[QCELL superview] superview];
path = [QTable indexPathForCell:QCELL];
}
else if(version >= 8.0)
{
UITableViewCell *QCELL = (UITableViewCell *)[[[textField superview]superview]superview];
QTable = (UITableView *)[[QCELL superview] superview];
path = [QTable indexPathForCell:QCELL];
}
NSIndexPath*path = = [QTable indexPathForCell:QCELL];
return true;
}
I change your original code a little and came out with this:
I made this change because I had a problem when the version was 8 the if for the >= 7 was been fired. And I need the variables outside the if.
CGFloat version =[[[UIDevice currentDevice] systemVersion] floatValue];
UITableViewCell *cell;
UITableView *table;
if ((version > 7) && (version < 8))
{
UITableViewCell *cell = (UITableViewCell*)[[[sender superview] superview]superview];
table = (UITableView *)cell.superview.superview;
}
else if(version > 8)
{
UITableViewCell *cell = (UITableViewCell*)[[sender superview] superview];
table = (UITableView *)cell.superview.superview;
}
Similarly I had a button inside a UITableViewCell.This will work for you. Here buttonView means my UIButton instance
For iOS 8
UITableViewCell *cell = (UITableViewCell*)[buttonView superview];
gave me the UITableViewCell instance
and for iOS 7 as usual
UITableViewCell *cell = [buttonView superview]superview];
gave me the UITableViewCell instance
And the
[QTable indexPathForCell:cell];
Will give you the NSIndexPath(This part is ok in the given answer).
This is what you get for traversing the view hierarchy instead of using delegate methods from the cell itself.
Make the Cell itself respond to the delegate of the textview. Let the cell have it's own protocol (delegate methods), and let your TableViewController be it's delegate.
Then your code in the subclassed UITableViewCell-class will look something like this:
- (BOOL)textFieldShouldBeginEditing:(UITextField *)textField
{
[self.delegate textFieldDidBeginEditingForCell:self];
return true;
}
Then in your TableViewController:
- (void)textFieldDidBeginEditingForCell:(YourCustomCell*)cell
{
NSIndexPath *path = [QTable indexPathForCell:cell];
...
}

how to know indexpath of UITableView.visibleCells?

NSArray * arrayOfVisibleTVC=[BNUtilitiesQuick ListController].tableViewA.visibleCells;
NSLog(#"array : %#", arrayOfVisibleTVC);
will display
"<UITableViewCell: 0x76c22a0; frame = (0 0; 320 75); autoresize = W; layer = <CALayer: 0x76bf770>>",
"<UITableViewCell: 0x76cfec0; frame = (0 75; 320 75); autoresize = W; layer = <CALayer: 0x769d260>>",
"<UITableViewCell: 0xe245f70; frame = (0 150; 320 75); autoresize = W; layer = <CALayer: 0xe245ed0>>",
"<UITableViewCell: 0xe248980; frame = (0 225; 320 75); autoresize = W; layer = <CALayer: 0xe2488c0>>",
"<UITableViewCell: 0xe24afa0; frame = (0 300; 320 75); autoresize = W; layer = <CALayer: 0xe24aee0>>"
and with this
UITableViewCell * lastObjectOfArray=arrayOfVisibleTVC.lastObject;
int indexpathLastObject= (lastObjectOfArray.frame.origin.y/new.frame.size.height);
I know the last of arrayOfVisibleTVC.lastObject that I use to get indexpath. but I think there is an another way to get indexpath of TableViewcell that showed.. any one can help me?
so I can get indexpath of cell as integer
Here is method in UITableView class:
objective-c
- (NSArray *)indexPathsForVisibleRows
Swift
public var indexPathsForVisibleRows: [NSIndexPath]? { get }
UITableView has a method to find the indexPath of a cell:
- (NSIndexPath *)indexPathForCell:(UITableViewCell *)cell
Just iterate over your visible cells array and pass each cell to get the index path.
for (UITableViewCell *cell in arrayOfVisibleTVC) {
NSIndexPath *path = [[BNUtilitiesQuick ListController].tableViewA indexPathForCell:cell];
NSLog(#"%#", path);
}

Resources