I'm building a messaging screen as part of the app I'm writing. Currently, it is a UITableView containing cells that are my own custom subclass of UITableViewCell. I am using auto layout with constraints on the cell defined in Interface Builder. My messaging mimics, or attempts to mimic, the default messaging app. There are three main components of each table view cell: a UITextView containing the message body and two additional UILabels, one for the sender's name and/or time stamp, and the other for delivered/read receipts.
Now, using auto layout combined with tableView:heightForRowAtIndexPath: on my view controller, the message's text view in each table view cell is supposed to grow according to how large the message is (I use sizeWithFont:constainedToSize:lineBreakMode at present - I know it's deprecated but the replacements don't work on iOS 6 and are also flaky as of yet). This works fine when both labels and the text view are all present on the UI. However, in an individual message thread, I remove the delivered/read label using removeFromSuperview for all message cells but the final message (if said final message is sent by you). This does not cause adverse affects on iOS 7, but on iOS 6, any cell that has a label removed causes the text view to have a height of 0.0 (confirmed by debug outputs). Programmatically re-adding the label and appropriate auto layout constraints seems to fix it, but in any cell where that label is removed, even if I calculate a positive height for the text view in tableView:heightForRowAtIndexPath:, the text view height is zero, and the remaining label ends up shifted upwards to 'appear' to overwrite the text view.
I guess removing a view from its superview is the main culprit here, but I don't understand why this would occur only on iOS 6 instead of both 6 and 7.
Now, the code. Here is my cellForRowAtIndexPath: and heightForRowAtIndexPath: methods
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString * sentMessageCellIdentifier = #"sentMessageCell";
static NSString * receivedMessageCellIdentifier = #"receivedMessageCell";
MessageCell * cell;
Message * messageObject = [associatedThread.messages objectAtIndex:indexPath.row];
GroupMember * selfMm = [associatedThread.parentGroup groupMemberForUser:[ApplicationInstance getInstance].currentUser];
if ([messageObject.sender isEqualToGroupMember:selfMm]) {
// Sent
cell = (MessageCell *) [tableView dequeueReusableCellWithIdentifier:sentMessageCellIdentifier];
cell.sentTimeLabel.text = [UtilityFunctions messageFriendlyFormattedDateTimeForDate:messageObject.messageTime];
if ([messageObject isEqualToMessage:[associatedThread.messages lastObject]]) {
cell.deliveredReadByLabel.text = #"Sent";
} else {
cell.deliveredReadByLabel.text = nil;
}
} else {
// Received
cell = (MessageCell *) [tableView dequeueReusableCellWithIdentifier:receivedMessageCellIdentifier];
[cell setSenderAndDateTimeForSender:messageObject.sender date:messageObject.messageTime];
}
// Read by label
NSString * readByText = nil;
if (associatedThread.parentGroupMember == nil) {
// Group thread
if (messageObject.readBy.count == 0) {
if (![messageObject.sender isEqualToGroupMember:selfMm]) {
readByText = #"Read by: only you";
}
} else {
NSInteger readByCount = messageObject.readBy.count;
NSInteger toSubtract = [messageObject.sender isEqualToGroupMember:selfMm] ? 1 : 2;
if (readByCount == associatedThread.members.count - toSubtract) { // If everyone read it (minus you and the sender)
readByText = #"Read by everyone";
} else {
GroupMember * randRbm = [messageObject.readBy firstObject];
if (messageObject.readBy.count == 1) {
cell.deliveredReadByLabel.text = [NSString stringWithFormat:#"Read by: %#", randRbm.user.displayName];
} else if (messageObject.readBy.count > 1) {
cell.deliveredReadByLabel.text = [NSString stringWithFormat:#"Read by: %# + %d", randRbm.user.displayName, messageObject.readBy.count - 1];
}
cell.deliveredReadByLabel.userInteractionEnabled = YES;
[cell.deliveredReadByLabel addGestureRecognizer:[[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(didTapReadByLabel:)]];
}
}
} else {
// One-on-one individual thread
if ([messageObject isEqualToMessage:[associatedThread.messages lastObject]] &&
[messageObject.sender isEqualToGroupMember:selfMm]) {
if (cell.deliveredReadByLabel.superview == nil) {
[cell.contentView addSubview:cell.deliveredReadByLabel];
// Auto-layout bindings
NSArray * constaints = #[[NSLayoutConstraint constraintWithItem:cell.deliveredReadByLabel
attribute:NSLayoutAttributeTop
relatedBy:NSLayoutRelationEqual
toItem:cell.sentTimeLabel
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:1.0],
[NSLayoutConstraint constraintWithItem:cell.deliveredReadByLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeLeft
multiplier:1.0
constant:20.0],
[NSLayoutConstraint constraintWithItem:cell.deliveredReadByLabel
attribute:NSLayoutAttributeRight
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeRight
multiplier:1.0
constant:-20.0],
[NSLayoutConstraint constraintWithItem:cell.deliveredReadByLabel
attribute:NSLayoutAttributeBottom
relatedBy:NSLayoutRelationEqual
toItem:cell.contentView
attribute:NSLayoutAttributeBottom
multiplier:1.0
constant:-5.0]
];
[cell addConstraints:constaints];
}
if (messageObject.readBy.count == 1) {
readByText = #"Read";
}
} else {
[cell.deliveredReadByLabel removeFromSuperview];
}
}
if (readByText != nil) {
cell.deliveredReadByLabel.text = readByText;
}
debugLog(#"%#", [messageObject isEqualToMessage:[associatedThread.messages lastObject]] ? #"YES" : #"NO");
debugLog(#"x,y [%f, %f] | w,h [%f, %f] - message view", cell.messageView.frame.origin.x, cell.messageView.frame.origin.y, cell.messageView.frame.size.width, cell.messageView.frame.size.height);
debugLog(#"x,y [%f, %f] | w,h [%f, %f] - sent time label", cell.sentTimeLabel.frame.origin.x, cell.sentTimeLabel.frame.origin.y, cell.sentTimeLabel.frame.size.width, cell.sentTimeLabel.frame.size.height);
debugLog(#"x,y [%f, %f] | w,h [%f, %f] - sender time label", cell.senderAndDateTimeLabel.frame.origin.x, cell.senderAndDateTimeLabel.frame.origin.y, cell.senderAndDateTimeLabel.frame.size.width, cell.senderAndDateTimeLabel.frame.size.height);
debugLog(#"x,y [%f, %f] | w,h [%f, %f] - delivered label", cell.deliveredReadByLabel.frame.origin.x, cell.deliveredReadByLabel.frame.origin.y, cell.deliveredReadByLabel.frame.size.width, cell.deliveredReadByLabel.frame.size.height);
// Message body
[UtilityFunctions setZeroInsetsForTextView:cell.messageView];
cell.messageView.text = messageObject.messageBody;
cell.messageView.scrollEnabled = YES;
cell.messageView.scrollEnabled = NO;
return cell;
}
- (CGFloat) tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *) indexPath {
CGFloat totalHeight = 0.0;
Message * m = [associatedThread.messages objectAtIndex:indexPath.row];
// Top and bottom padding
totalHeight += 5.0 + 5.0;
// Height + padding between labels (and text view)
totalHeight += 14.0 + 14.0 + 1.0 + 1.0; // height + height + padding + padding
// Modify UI slightly if incoming message and one-on-one thread:
if (associatedThread.parentGroupMember != nil) {
totalHeight -= (14.0 + 1.0);
if ([m isEqualToMessage:[associatedThread.messages lastObject]]) {
if ([m.sender isEqualToGroupMember:[associatedThread.parentGroup groupMemberForUser:[ApplicationInstance getInstance].currentUser]]) {
totalHeight += (14.0 + 1.0);
}
}
}
NSString * bodyText = m.messageBody;
CGSize constraint = CGSizeMake(MESSAGE_TEXT_WIDTH_MAX, CGFLOAT_MAX);
CGSize sizeWithFont = [bodyText sizeWithFont:[UIFont systemFontOfSize:16.0] constrainedToSize:constraint lineBreakMode:NSLineBreakByWordWrapping];
totalHeight += sizeWithFont.height + 1.0; // 1.0 because iOS hates me
if ([m isEqualToMessage:[associatedThread.messages lastObject]]) {
debugLog(#"YES");
} else {
debugLog(#"NO");
}
debugLog(#"height: %f", totalHeight);
return totalHeight;
}
Here are the constraints I set in Interface Builder. Note the static width of the message text view:
And here is how it looks in iOS 6 (notes: the colors are for my own visual aid, it obviously will not stay that way, and simulator/device produces same results):
Here is the expected behavior in iOS 7, as I desire it to behave:
It's important to note the the height of the actual table view cell itself appears to be correct, but the text view is not adjusting accordingly, despite being able to I've tried adjusting my code in both methods above and attempted different techniques to no avail. I'm fairly certain that I need to use removeFromSuperview as that is the only way to both use auto layout and accommodate what I am trying to do. The delivered/read label gets removed under the following conditions:
There are two people in the message thread
The message is the latest message in the thread
That last message was sent by you
I know this is a terribly specific question, but if anyone has ideas as to why this occurs I would be grateful. Note that the text view is not editable, though it is selectable.
As always, thanks.
Edit: I also occasionally will get iOS 6 to throw Assertion failure in -[UITableViewCell layoutSublayersOfLayer:] even though my custom subclass doesn't implement that function. It has whined about constraints before, but that is a crap shoot to reproduce that error.
I solved this by giving in and just keeping both UILabels on the cell at all times. I'll just be clever about how I arrange the date and read bys. I'll leave this in case someone has a similar issue in the future.
Related
I have a custom UITableViewCell with UITextView, UICollectionView and UITableView inside. They have a 10 px space between. In some cases I have only UITextView, sometimes only collection view etc.
When no text - I have to move UICollection view up by 10 px. And If I have no text, tableView and collectionView it should be zero height.
- (void)configureMiddleWrapperState
{
self.middleWrapperHasNote = self.noteTextView.text.length > 0;
self.noteTextViewTopConstraint.constant = self.middleWrapperHasNote ? 7.f : 0.f;
self.staffWrapperTopConstraint.constant = self.middleWrapperHasStaff ? 7.f : 0.f;
self.tagsWrapperTopConstraint.constant = self.middleWrapperHasTags ? 7.f : 0.f;
BOOL middleWrapperHasAtleastOne = self.middleWrapperHasNote || self.middleWrapperHasStaff ||
self.middleWrapperHasTags;
self.middleWrapperLastTenPixelSpaceConstraint.constant = middleWrapperHasAtleastOne ? 7.f : 0.f;
[self setNeedsUpdateConstraints];
}
- (void)layoutSubviews
{
[super layoutSubviews];
[self setExcludedPaths];
[self configureMiddleWrapperState];
}
The result isn't like expected:
instead of one line.
When only text is looks like expected.
Constraints become work only if I scroll up and down many times.
try add
[self setNeedsLayout];
after
[self setNeedsUpdateConstraints];
I've found the issue. Coz my self.middleWrapperHasTags doesn't set correctly when reuse.
There is a UITableview subclass, that has *.xib file, with configured autolayout in it (like the image below, in xib all dependencies are configured for 2 label-style). This cell can calculate it's height dynamically, as usual autolayout cell.
But there is a case, when according to received data,the number of labels can vary - dependent of data in model.
Is there a way to add several labels (3,5,n) as subviews to cell, with existing autolayout system? It's important because cell should not lose ability to self-calculate its size
Since you are using .xibs (great for several reasons) the safest, least-cumbersome and most performant way is to register your .xib with different resue identifiers. Best to scan your model to find out and register which ones you'll need (err on the side of registering too many; make a mistake the other way and instant crash). In a subclass, override -initWithStyle:reuseIdentifier: and do setup there.
There are a couple ways to do setup.
IB heavy: Create a cell with the maximum number of labels. Attach them to each other, and attach each one to the bottom of the cell content view with a separate constraint, and rank the constraints in priority with 1000 being the label attached to the bottom with none removed and priorities going down from there. Then remove the labels you don't want in -initWithStyle:reuseIdentifier:; their constraints will be removed, allowing the next-lowest priority to go into effect.
Looping: For an arbitrary number, you can add the labels and their constraints in a -initWithStyle:reuseIdentifier: -- resuse identifier could be +stringWithFormat for the loop number, which would let you get the number with -intValue. This may be too cutsy though, so you might want to try creating a method -formatWithNumberOfLabels and checks a -didSetup flag.
EDIT: Option 2 was far more cumbersome than I expected. This example is programmatic, but you should be able to see how to adapt it for .xibs. In the cell's impementation:
-(id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
self = [super initWithStyle:style reuseIdentifier:reuseIdentifier];
if (self) {
[self.contentView setTranslatesAutoresizingMaskIntoConstraints:NO];
NSMutableDictionary *layoutDictionary = [[NSMutableDictionary alloc] init];
NSMutableString *visualFormatLanguageString = [[NSMutableString alloc] init];
NSInteger numberOfLabels = reuseIdentifier.integerValue; //This is not that safe, using below class method is the only way to make it safe to do this.
for (int i = 0; i <= numberOfLabels; i++) {
if (i == 0) {
[visualFormatLanguageString appendString:#"V:|-20-"];
} else {
[visualFormatLanguageString appendString:#"-8-"];
}
UILabel *labelX = [[UILabel alloc] init];
[labelX setTranslatesAutoresizingMaskIntoConstraints:NO];
int tag = 1000 + i;
labelX.tag = tag;
NSString *labelXString = [NSString stringWithFormat:#"Label%i", tag];
[layoutDictionary setObject:labelX forKey:labelXString];
[visualFormatLanguageString appendString:[NSString stringWithFormat:#"[%#]", labelXString]];
[self.contentView addSubview:labelX];
}
[visualFormatLanguageString appendString:#"-20-|"];
[self.contentView addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:visualFormatLanguageString
options:NSLayoutFormatAlignAllLeading
metrics:nil
views:layoutDictionary]];
}
return self;
}
+ (NSString*)reuseStringForNumber:(NSInteger)reuseNumber {
return [NSString stringWithFormat:#"%li",(long) reuseNumber];
}
In the TableView Data Source you must -- for the maximum number of labels you think you need - register:
- (void)viewDidLoad {
[super viewDidLoad];
for (NSInteger i = 0; i < 4; i++) {
[self.tableView registerClass:[MultiLabelTableViewCell class] forCellReuseIdentifier:[MultiLabelTableViewCell reuseStringForNumber:i]];
}
self.tableView.estimatedRowHeight = 44;
}
For an example that creates number of labels (row % 4) + 1:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
NSInteger cellNumber = indexPath.row % 4;
MultiLabelTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:[MultiLabelTableViewCell reuseStringForNumber:cellNumber] forIndexPath:indexPath];
for (int i = 0; i <= cellNumber; i++) {
int tag = 1000 + i;
[(UILabel *)[cell.contentView viewWithTag:tag] setText:[NSString stringWithFormat:#"Cell %i, label %i", indexPath.row, i]];
}
return cell;
}
Then just call the reuseIdentifier you need in cell for row at index path, and both you and the system will have to do the minimum work to get the correct height and layout for your cell.
I have two UILabels next to each other in row with left and right adjustments so that it looks like below.
|-Some text left adjusted----------some other text right adjusted-|
Both labels have adjustsFontSizeToFitWidth = YES and are linked to each other with the following constraint
[NSLayoutConstraint constraintWithItem:_rightLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:_leftLabel
attribute:NSLayoutAttributeRight
multiplier:1
constant:10]
So that they take up as much space as they can and if there is not enough space for the original font size it will be lowered thanks to adjustsFontSizeToFitWidth so that no text is truncated.
My problem is that when one needs to lower its font size due to long text i want the other label to lower its font size as well so that both are the same size instead of one being perhaps twice the size of the other. I would like to constraint the font size as well to match but alas i do not know how to this, any ideas?
From the UILabel documentation on adjustsFontSizeToWidth:
Normally, the label text is drawn with the font you specify in the font property. If this property is set to YES, however, and the text in the text property exceeds the label’s bounding rectangle, the receiver starts reducing the font size until the string fits or the minimum font size is reached.
I infer from this that the updated font is calculated at drawing time, and the font property is only read, not written to. Therefore, I believe the suggestion by Andrew to use KVO on the font property will not work.
Consequently, to achieve the result you want, you'll need to calculate the adjusted font size.
As Jackson notes in the comments, this very convenient NSString method to get the actual font has been deprecated in iOS 7. Technically, you could still use it until it's removed.
Another alternative is to loop through font scales until you find one that will fit both labels. I was able to get it working fine; here's a sample project that shows how I did it.
Also, here's the code in case that link ever stops working:
- (void)viewDidLoad
{
[super viewDidLoad];
NSLayoutConstraint *constraint = [NSLayoutConstraint constraintWithItem:_rightLabel
attribute:NSLayoutAttributeLeft
relatedBy:NSLayoutRelationGreaterThanOrEqual
toItem:_leftLabel
attribute:NSLayoutAttributeRight
multiplier:1
constant:10];
[self.view addConstraint:constraint];
}
- (IBAction)makeRightLabelLongerPressed:(id)sender {
self.rightLabel.text = #"some much longer right label text";
}
- (IBAction)adjustLabelSizes:(id)sender {
NSLog(#"Attempting to adjust label sizes…");
CGFloat minimumScaleFactor = fmaxf(self.rightLabel.minimumScaleFactor, self.leftLabel.minimumScaleFactor);;
UIFont * startingFont = self.rightLabel.font;
for (double currentScaleFactor = 1.0; currentScaleFactor > minimumScaleFactor; currentScaleFactor -= 0.05) {
UIFont *font = [startingFont fontWithSize:startingFont.pointSize * currentScaleFactor];
NSLog(#" Attempting font with scale %f (size = %f)…", currentScaleFactor, font.pointSize);
BOOL leftLabelWorks = [self wouldThisFont:font workForThisLabel:self.leftLabel];
BOOL rightLabelWorks = [self wouldThisFont:font workForThisLabel:self.rightLabel];
if (leftLabelWorks && rightLabelWorks) {
NSLog(#" It fits!");
self.leftLabel.font = font;
self.rightLabel.font = font;
return;
} else {
NSLog(#" It didn't fit. :-(");
}
}
NSLog(#" It won't fit without violating the minimum scale (%f), so set both to minimum. Some text may get truncated.", minimumScaleFactor);
UIFont *minimumFont = [self.rightLabel.font fontWithSize:self.rightLabel.font.pointSize * self.rightLabel.minimumScaleFactor];
self.rightLabel.font = minimumFont;
self.leftLabel.font = minimumFont;
}
- (BOOL) wouldThisFont:(UIFont *)testFont workForThisLabel:(UILabel *)testLabel {
NSDictionary *attributes = [NSDictionary dictionaryWithObjectsAndKeys:testFont, NSFontAttributeName, nil];
NSAttributedString *as = [[NSAttributedString alloc] initWithString:testLabel.text attributes:attributes];
CGRect bounds = [as boundingRectWithSize:CGSizeMake(CGRectGetWidth(testLabel.frame), CGFLOAT_MAX) options:(NSStringDrawingUsesLineFragmentOrigin) context:nil];
BOOL itWorks = [self doesThisSize:bounds.size fitInThisSize:testLabel.bounds.size];
return itWorks;
}
- (BOOL)doesThisSize:(CGSize)aa fitInThisSize:(CGSize)bb {
if ( aa.width > bb.width ) return NO;
if ( aa.height > bb.height ) return NO;
return YES;
}
This approach could be trivially refactored into a category method that replaces the deprecated method linked to by Jackson.
You can solve this problem this way:
Swift 5
extension UILabel {
var actualFontSize: CGFloat {
guard let attributedText = attributedText else { return font.pointSize }
let text = NSMutableAttributedString(attributedString: attributedText)
text.setAttributes([.font: font as Any], range: NSRange(location: 0, length: text.length))
let context = NSStringDrawingContext()
context.minimumScaleFactor = minimumScaleFactor
text.boundingRect(with: frame.size, options: .usesLineFragmentOrigin, context: context)
let adjustedFontSize: CGFloat = font.pointSize * context.actualScaleFactor
return adjustedFontSize
}
}
Usage:
firstLabel.text = firstText
secondLabel.text = secondText
view.setNeedsLayout()
view.layoutIfNeeded()
let smallestSize = min(firstLabel.actualFontSize, secondLabel.actualFontSize)
firstLabel.font = firstLabel.font.withSize(smallestSize)
secondLabel.font = secondLabel.font.withSize(smallestSize)
You could try using key-value observing to observe changes to the font property on one label and when it does, set the other label to use the same font.
In your -viewDidLoad method:
// Add self as an observer of _rightLabel's font property
[_rightLabel addObserver:self forKeyPath:#"font" options:NSKeyValueObservingOptionNew context:NULL];
In the same controller implementation (self in the above code snippets context):
// Observe changes to the font property of _rightLabel
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
if (object == _rightLabel && [keyPath isEqualToString:#"font"]) {
// Set _leftLabel's font property to be the new value set on _rightLabel
_leftLabel.font = change[NSKeyValueChangeNewKey];
}
}
I found a better solution. Just add to ur text spaces from begin to end to uilabel, which have less text length. And font will same.
there are two labels in view. my designer will offer me markman as following
However, I can only set the constraint in Xcode like following
I mean, the designer need me to align the two labels with baseline, however, I can set the space of two labels with frame. Is there a way to set space of two labels with baseline instead of label's frame using Autolayout?
thanks #Arkadiusz, #Fogmeister, I have implemented a NSLayoutConstraint category to do it.
#implementation NSLayoutConstraint (MarkmanSpace)
- (void)updateSpaceBetweenTopView:(UIView *)topView bottomView:(UIView *)bottomView {
UIFont *topViewFont = [self fontFromView:topView];
UIFont *bottomViewFont = [self fontFromView:bottomView];
if(topViewFont && bottomViewFont) {
CGFloat padding = fabs(topViewFont.descender) + fabs([self lowercaseGlyphTopBaselineHeightForFont:bottomViewFont]);
self.constant -= padding;
}
}
- (UIFont *)fontFromView:(UIView *)view {
UIFont *viewFont = nil;
if([view isKindOfClass:[UILabel class]]) {
viewFont = ((UILabel *)view).font;
}
return viewFont;
}
- (CGFloat)lowercaseGlyphTopBaselineHeightForFont:(UIFont *)font {
return font.ascender - font.xHeight;
}
#end
but there's a problem ,which is the font.xHeight is lower the actual height for Chinese Character,
Your designer wants you to use a baseline and an x-height, see http://en.wikipedia.org/wiki/Baseline_(typography). A format option for the x-height isn't available in Auto Layout (see NSLayoutFormatOptions), so I don't think it's possible.
I have a popover screen, with inside it :
a label, that may or may not appear (title)
a search bar, that may or may not appear
a label, that may or may not appear, and has a variable height (help label)
a scrollview, that may or may not appear, and has a variable height (some infos about the following table)
a table view
In order to present something nice, in viewDidLoad, I move the various frames to place the objects correctly and not have unused spaces cluttering my popover. Besides, I then resize the table (to take the most place needed), and the popover via contentSizeInPopover (to avoid having a near-empty huge popover). All that resizing seems to work nicely, but I have one big problem : with all that resizing done, some cells of my UITableView become unresponsive. One or two cells, usually the second one, only respond if i tap in their outer corners, but the rest of the cell completely ignore any touches.
I've tried everything : moving all to viewWillAppear, letting the autoresize do its job (doesn't seem to work either), but I still have this problem every time. I've found that if I comment the lines involved with changing the frame of the table, or the ones in contentSizeInPopover, the problem stops, but then my view is messed up, so this ins't a fix.
If anyone could give me something to get out of this mess, that would be awesome.
- (CGFloat)getHeightWithoutTable {
return LIST_TITLE_HEIGHT + (self.searchBar.hidden ? 0 : LIST_SEARCH_BAR_HEIGHT) + (self.helpLabel.hidden ? 0 : self.helpLabel.frame.size.height + LIST_STD_SPACE) + (self.errorScrollView.hidden ? 0 : self.errorScrollView.frame.size.height + LIST_STD_SPACE);
}
-(void)viewDidLoad {
[super viewDidLoad];
self.tableViewOutlet.backgroundView = nil;
self.originData = [NSMutableArray array];
self.searchedData = [NSMutableArray array];
if (self.helper != nil) {
CGFloat heightOffset = 0;
// Content
self.originData = [self.helper getData];
self.tableData = [NSMutableArray arrayWithArray:self.originData];
// Title
NSString *title = [self.helper getPopoverTitle];
if (title == nil) {
self.popoverTitle.hidden = YES;
heightOffset -= LIST_TITLE_HEIGHT;
} else {
self.popoverTitle.text = [self.helper getPopoverTitle];
}
// Search
if ([self.originData count] [self getStdHeight] / 3){
self.helpLabel.lineBreakMode = UILineBreakModeTailTruncation;
[self.helpLabel sizeThatFits:CGSizeMake(self.helpLabel.frame.size.width, [self getStdHeight] / 3)];
}
heightOffset += (self.helpLabel.frame.size.height - LIST_HELP_STD_HEIGHT);
}
// Errors
if ([self.helper respondsToSelector:#selector(getErrors)]) {
self.errors = [self.helper getErrors];
}
if (self.errors == nil || [self.errors count] == 0) {
self.errorScrollView.hidden = YES;
self.errorBg.hidden = YES;
heightOffset -= LIST_ERROR_STD_HEIGHT + LIST_STD_SPACE;
} else {
[self createErrorView];
heightOffset += (self.errorScrollView.frame.size.height - LIST_ERROR_STD_HEIGHT);
}
// Table
CGFloat previewHeight = LIST_CELL_HEIGHT * [self.tableData count] + LIST_STD_SPACE;
CGFloat remainingHeight = LIST_MAX_HEIGHT - [self getHeightWithoutTable] - LIST_STD_SPACE;
CGFloat tableHeight = MIN(previewHeight, remainingHeight);
CGRect tableFrame = self.tableViewOutlet.frame;
self.tableViewOutlet.frame = CGRectMake(tableFrame.origin.x, tableFrame.origin.y + heightOffset, LIST_WIDTH, tableHeight);
// Selected items
if ([helper getSelectedObject] != nil){
int index = [self.tableData indexOfObject:[helper getSelectedObject]];
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:index inSection:0];
[self.tableViewOutlet scrollToRowAtIndexPath:indexPath atScrollPosition:UITableViewScrollPositionTop animated:YES];
}
}
}
- (CGSize)contentSizeForViewInPopover {
if (self.navigationController) {
return CGSizeMake(LIST_WIDTH, LIST_MAX_HEIGHT);
} else {
CGFloat totalHeight = [self getHeightWithoutTable] + self.tableViewOutlet.frame.size.height + LIST_STD_SPACE;
return CGSizeMake(LIST_WIDTH, totalHeight);
}
}
(gist if you need some coloring to help you)
An image of the nib :
Just a shot in the dark, since you have not provided any code. If you are adding things to the UITableCellView, just remember that a lot of components have their UserInteractionEnabled set to NO, which will disable the ability to interact with it. Make sure that any items you add to the cell that potentially take up the space where you are tapping (presumably the center of the cell?) have their UserInteractionEnabled set to YES.
The reason why the edges might still work is that the UITableCellView consists of 3 main parts, so you are probably only changing the center part.
Post some code then we can have a better look.
Found the answer myself : the fact I was using a self-filled UIScrollView next to my UITableView seemed to be the problem. As soon as I replaced the UIScrollView by a proper UITableView, the problem disappeared.