I am working with interface builder to create a xib. This xib has a uiview that contains a uitextview. Both are supposed to resize as the text in the uitextview changes. The constraints look a lot like this:
The pink UITextView pushes on the blue superview. The blue uiView has a minimum width of 189 px and a trailing constraint of at least 8px.
For the most part this works. Really long sections of text resize the two views to the fullest extent allowed as intended and if there are only one or two words, the views stay small. However, the problem is when you have a short sentence.
In this case, the views only expand to about 189px, and the text moves to the second line even though there is space to expand.
Here is what it looks like when you only put a few words in:
and here is a fully expanded box:
I have tried to make the trailing constraint have a lower priority than the others, and I have tried modifying the content hugging and compression resistance properties in many ways without success.
How can I make the views expand so that they fit the text content with the fewest number of lines? There are no restrictions on height, only on width.
Any help would be appreciated!
Get the new size of the textView using this method
CGSize sz = [_textView.text sizeWithFont:_textView.font]
in case that didn't work very well with the height,get the width you just got from the preivous method and use in the next method to get the appropriate height you need
- (CGFloat)textViewHeightForAttributedText:(NSAttributedString *)text andWidth:(CGFloat)width
{
UITextView *textView = [[UITextView alloc] init];
[textView performSelectorOnMainThread:#selector(setAttributedText:)
withObject:text
waitUntilDone:YES];
CGSize size = [textView sizeThatFits:CGSizeMake(width, FLT_MAX)];
return size.height;
}
The key is to set the preferredMaxLayoutWidth to something big (320px sounds good. Your right constraint is going to limit it anyways).
You can do that in your code as
[self.label setPreferredMaxLayoutWidth:320];
Or from the Interface Builder as follows:
This way you'll see the label expanding as expected:
Have you tried the solution presented in this post?
Dynamic expand UITextView on ios 7
I believe you have to set your UITextView to sizeToFit
[YourUITextView sizeToFit];
Related
I am creating a table header view with two UILabels. The constraints look like this:
The top UILabel is attached to the top, leading and trailing edges of the container. The bottom UILabel has the leading and trailing edges aligned to the top label, and the bottom edge to the bottom of the container. There is also a vertical spacing constraint between the two UILabels. All views translatesAutoresizingMaskIntoConstraints have been set to NO of course. BTW, the whole thing is made in code.
I calculate the height of the UIView by getting the intrinsic content size of each label and padding so that I can create the rect of the container view and add it to the UITableView. Like this:
-(float)calculateHeightwithMaxWidth:(float)maxWidth
{
float totalHeight = 0;
const float containerPadding = 30;
const float maxHeight = 1000;
maxWidth = maxWidth - containerPadding;
UIFont *nameFont = [UIFont fontWithName:#"AvenirNext-Regular" size:18];
UIFont *descriptionFont = [UIFont fontWithName:#"AvenirNext-Regular" size:13];
CGSize nameSize = [_productNameLabel.text gsSizeWithFont:nameFont
withMaximumSize:CGSizeMake(maxWidth, maxHeight)];
CGSize descriptionSize = [_productDescriptionLabel.text gsSizeWithFont:descriptionFont
withMaximumSize:CGSizeMake(maxWidth, maxHeight)];
totalHeight = nameSize.height + descriptionSize.height + containerPadding;
return totalHeight;
}
This code works perfectly in iOS 7 and has been working for several versions. Now that I'm testing in iOS 8 I get a crash with unsatisfying constraints. If I see the constraints of the view before calling layoutIfNeeded everything looks great, but after calling it I see two new constraints, which are the width and height constraints of the container view. I never created this constraints, and in iOS 7 never needed to.
Even after creating these constraints myself to fix this, I get even more errors trying to break them. Am I missing something? Did the logic for constraints change in iOS 8?
Thanks!
The problem is that you are asking for too much precision. You don't know exactly what height the auto layout system will give (you are just adding up some numbers that you think will give the same result), and so when you assign the header view a fixed height, if it doesn't match the system's own calculation perfectly, right down to the last decimal place, the constraints can't be satisfied. You should never have been doing it that way in the first place; you are mixing apples with oranges (manual calculation with the system's autolayout). The system may, for example, apply rounding of which you can know nothing (in order to keep the rects integral, etc.). Who knows what it does? I'm amazed that this ever worked.
You have two much better choices:
Wrap the pair of views in a container view (looks like you've done that) and just ask the container view for its systemLayoutSizeFittingSize. This tells you exactly what the system will do. In other words, instead of you calculating (which is hit or miss), ask the system to calculate.
Even better, allow yourself some slack: make one of the height spacer constraints an inequality or a lower priority, so that when you apply your fixed height that constraint has permission to grow or shrink.
I know that in a view-based table view, the row class NSTableCellView subclasses from NSView. This class contains two properties, an NSTextField and an NSImageView. I am only using the NSTextField without an image view. However, some cells in my table view must contain multiple lines of text, while others may only contain one or two lines. I need to be able to resize individual NSTableCellView views depending on the size of their NSTextField textField property.
Therefore, I needed to do the following:
Get the frameSize of the NSTextField in the table cell view.
Set the frameSize of the NSTableCellView to the frameSize of the NSTextField (the one we got in set one)
However, this approach hasn't been working. I have begun to think that my approach to resize the NSTableCellView is incorrect. Here is the code that I have been using:
[tableCellView setFrameSize:[[tableCellView textField] frame].size];
[tableCellView setNeedsDisplay:YES];
Is there a problem with this approach? I would expect the cell to resize, but it doesn't? What is going wrong?
Thanks.
[edit] I should have started by commenting that the size of the textField has little to do with how large it would need to be to display all of its content.
I use this code to determine the height of a string based on the width of a table cell:
- (CGFloat) displayStringHeightWithWidth:(CGFloat)width
{
CGSize size = NSMakeSize(width,0);
NSRect bounds = [self.displayString boundingRectWithSize:size
options:NSStringDrawingUsesLineFragmentOrigin|NSStringDrawingUsesFontLeading];
return bounds.size.height;
}
Ideally you can adapt that to finding the height of the textField.stringValue or textField.attributedStringValue. Not that the above is also from OSX, not iOS, so YMMV on some of the fluff.
So that changes your algorithm to:
Get the width of the table column
Get the height of the required bounding rect for the textfield's text
Tell the tableView that the row height is whatever you found in 2
Now. Regarding #3. I believe that you have to use the tableView:heightOfRow: in NSTableViewDelegate protocol as well as call the table's noteHeightOfRowsWithIndexesChanged: to have row heights change. The tableView's not otherwise aware that the height of your cell has changed. Note the discussion in the documentation. It could be your method would work without the delegate and just telling the table that the row heights for the rows that you are changing are dirty... but I wouldn't really expect it.
I have a small question, I have a code to generate 200 UIImages in random positions and rotations.
I need to detect the touch event on these images, but I need to check if the touched UIImage is not covered by any other image (even if they intersect in a small area).
Can anybody help me on this?
BTW : I'm trying to do something similar to this game : http://www.dressup247.com/game/1014/Bank-Note-Stack.html
I would suggest to make all images as UIButton's with background image and set for all buttons one action. One more advise - set tag order for every button from most low-lying to most high-lying (and store biggest tag in some iVar). You can do it when layout is generated. It allows you to detect how many views lay over tapped view.
Use CGRectIntersectsRect intersecting views.
-(IBAction)banknotTapped:(UIButton*)sender{
int tag = sender.tag
NSMutableArray *highestViews = [NSMutableArray array];
for (int i=sender.tag; i < biggestTag; ++i){
UIView *v = [self.view viewWithTag:i];
if (CGRectIntersectsRect(sender.frame, v.frame) ) // it allows to find intersecting views
[highestViews addObject:v];
}
}
Now highestViews will contain all highest views that intersect with sender.
Note: tags allow you to detect views, but if you will delete images from superview then it can lead to problems since tag order will broken. Hide views instead of deleting or follow #Costique method in order to determine views order.
You can use the following snippet to determine the order of a subview in its parent view:
NSUInteger order = [containerView.subviews indexOfObjectIdenticalTo: subview];
The less the order, the "higher" the subview is. The topmost subview will have zero order.
While I'd suggest that it would be faster to do this mathematically, defining the banknotes as rectangles and testing for overlap using something like the separating axes theorem, that's obviously not what you're asking and not suitable for general case images.
So I'd suggest that you create a CGBitmapContext the same size as your play area and that when seeding your play area, for each note you place you do something like:
assign new, as yet unused colour to the bank note
draw it to the bitmap context at its destination position, but as a solid object of the assigned colour — so you preserve the outline of the original shape but at each pixel you draw either the solid, assigned colour or no colour at all
count how many pixels in the entire context are now the assigned colour, store that with your object representing the note
Subsequently, when the game starts run through the buffer and count how many of each assigned colour. All notes that have the same number stored as are currently visible are on top. Whenever a note is removed, redraw all the others in order and do the colour count once.
Note that you're not doing the colour count on the total buffer once per note, just once in total. So it's a fixed cost and probably occurs less often than once per tap.
Probably the easiest way to do the drawing as a single colour is to create a mask version of each graphic when it's loaded and then to draw that with a suitable tint. There's an introduction to alpha masks here; you'll probably want to create a custom bitmap image context rather than using the implicit one returned by UIGraphicsGetCurrentContext and to post filter to test the output alpha — pushing down to 0 if its less than some threshold, up to 255 otherwise.
I'm using -[NSString sizeWithFont] to get the text height. The character 't' is clearly taller than 'm', but -sizeWithFont returns the same height for both these strings. Here's the code:
UIFont* myFont = [UIFont fontWithName:#"Helvetica" size:1000.0];
NSString* myStr = #"m";
CGSize mySize = [myStr sizeWithFont:myFont];
With 'm' as shown, it returns {834, 1151}. With myStr = #"t" instead, it's {278, 1151}. The smaller width shows up as expected, but not the height.
Does some other function wrap the text tightly? I'm ideally looking for something equivalent to Android's Paint.getTextBounds().
The information you get back from this method is basically the line height for the font (i.e., it's the ascent plus the descent for the font you've chosen). It's not based on individual characters, it's based on specific font metrics. You can get most of the information about a font's metrics from the UIFont class (e.g., the method -ascender gives you the height of the ascender for the font). Mostly, you will be dealing with the total amount of vertical space needed to draw the glyphs with the heights ascenders and the lowest descenders for that font. There is no way to get information about individual glyphs from UIFont. If you need this information, you'll have to look at the CoreText framework, which gives you a lot more flexibility in how you draw and arrange glyphs but is far, far more complicated to use.
For more information on dealing with text in your app, please se the Drawing and Managing Text section of the Text, Web, and Editing Programming Guide. It is also a good launching point for most of the frameworks and classes you'll need to deal with whether you go the UIKit or the CoreText route.
Hmmm... I assume you're only going to be working with individual characters then. sizeWithFont, as noted above, returns the height of the largest character of that font as the text field is going to be that height no matter what you do. You would be able to get the LARGEST height values (UIFont's CGFloat capHeight) however it looks like you're going to be working with all kinds of text
I would take a look at the CoreText framework. Start here and read away. Inevitably you're going to end up with something along the lines of this:
CGFloat GetLineHeightForFont(CTFontRef iFont)
{
CGFloat lineHeight = 0.0;
check(iFont != NULL);
lineHeight += CTFontGetLeading(iFont);
return lineHeight;
}
I am desperately trying to get rid of the margins around my table below:
I don't want the light gray area directly to the left and above "Brea" in the pic - I want the white table cells to extend left and up to completely cover the light grey area (to meet up with left-most white area)
Any help would be greatly appreciated!!!
Your table has the grouped style. You want the plain style. If you create it programmatically, do it like this:
UITableView *tv = [[UITableView alloc] initWithFrame:[self.view bounds]
style:UITableViewStylePlain];
Or in IB, in the attributes inspector,under TableView, select Style: Plain.
Old topic I know, but I was able to remove the edges and use a grouped view by setting the background view to nil. Psuedo code;
-(void) viewDidLoad {
self.tableView.backgroundView = nil;
}