Constraints in iOS 8 - objective-c

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.

Related

UItextview not expanding with constraints as expected

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];

Check if UIImage is on the top of view

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.

How do I calculate the exact height of text using UIKit?

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;
}

UIScrollView - snap to control when decelerating

The UIScrollView has a lot of information available to the programmer, but I dont see an obvious way to control the location that the control stop at after decelerating from a scroll gesture.
Basically I would like the scrollview to snap to specific regions of the screen. The user can still scroll like normal, but when they stop scrolling the view should snap to the most relevant location, and in the case of a flick gesture the deceleration should stop at these locations too.
Is there an easy way to do something like this, or should I consider the only way to accomplish this effect to write a custom scrolling control?
Since the UITableView is a UIScrollView subclass, you could implement the UIScrollViewDelegate method:
- (void)scrollViewWillEndDragging:(UIScrollView *)scrollView
withVelocity:(CGPoint)velocity
targetContentOffset:(inout CGPoint *)targetContentOffset
And then compute what the closest desired target content offset is that you want, and set that on the inout CGPoint parameter.
I've just tried this and it works well.
First, retrieve the unguided offset like this:
CGFloat unguidedOffsetY = targetContentOffset->y;
Then Figure out through some math, where you'd want it to be, noting the height of the table header. Here's a sample in my code dealing with custom cells representing US States:
CGFloat guidedOffsetY;
if (unguidedOffsetY > kFirstStateTableViewOffsetHeight) {
int remainder = lroundf(unguidedOffsetY) % lroundf(kStateTableCell_Height_Unrotated);
log4Debug(#"Remainder: %d", remainder);
if (remainder < (kStateTableCell_Height_Unrotated/2)) {
guidedOffsetY = unguidedOffsetY - remainder;
}
else {
guidedOffsetY = unguidedOffsetY - remainder + kStateTableCell_Height_Unrotated;
}
}
else {
guidedOffsetY = 0;
}
targetContentOffset->y = guidedOffsetY;
The last line above, actually writes the value back into the inout parameter, which tells the scroll view that this is the y-offset you'd like it to snap to.
Finally, if you're dealing with a fetched results controller, and you want to know what just got snapped to, you can do something like this (in my example, the property "states" is the FRC for US States). I use that information to set a button title:
NSUInteger selectedStateIndexPosition = floorf((guidedOffsetY + kFirstStateTableViewOffsetHeight) / kStateTableCell_Height_Unrotated);
log4Debug(#"selectedStateIndexPosition: %d", selectedStateIndexPosition);
NSIndexPath *indexPath = [NSIndexPath indexPathForRow:selectedStateIndexPosition inSection:0];
CCState *selectedState = [self.states objectAtIndexPath:indexPath];
log4Debug(#"Selected State: %#", selectedState.name);
self.stateSelectionButton.titleLabel.text = selectedState.name;
OFF-TOPIC NOTE: As you probably guessed, the "log4Debug" statements are just logging. Incidentally, I'm using Lumberjack for that, but I prefer the command syntax from the old Log4Cocoa.
After the scrollViewDidEndDecelerating: and scrollViewDidEndDragging:willDecelerate: (the last one just when the will decelerate parameter is NO) you should set the contentOffset parameter of your UIScrollView to the desired position.
You also will know the current position by checking the contentOffset property of your scrollview, and then calculate the closest desired region that you have
Although you don't have to create your own scrolling control, you will have to manually scroll to the desired positions
To add to what felipe said, i've recently created a table view that snaps to cells in a similar way the UIPicker does.
A clever scrollview delegate is definitely the way to do this (and you can also do that on a uitableview, since it's just a subclass of uiscrollview).
I had this done by, once the the scroll view started decelerating (ie after scrollViewDidEndDragging:willDecelerate: is called), responding to scrollViewDidScroll: and computing the diff with the previous scroll event.
When the diff is less than say a 2 to 5 of pixels, i check for the nearest cell, then wait until that cell has been passed by a few pixels, then scroll back in the other direction with setContentOffset:animated:.
That creates a little bounce effect that is very nice for user experience, as it gives a good feedback on the snapping.
You'll have to be clever and not do anything when the table is bouncing at the top or bottom (comparing the offset to 0 or the content size will tell you that).
It works pretty well in my case because the cells are small (about 80-100px high), you might run into problems if you have a regular scroll view with bigger content areas.
Of course, you will not always decelerate past a cell, so in this case i just scroll to the nearest cell, and the animation looks jerky. Turns out with the right tuning, it barely ever happens, so i'm cool with this.
Spend a few hours tuning the actual values depending on your specific screen and you can get something decent.
I've also tried the naive approach, calling setContentOffset:animated: on scrollViewDidEndDecelerating: but it creates a really weird animation (or just plain confusing jump if you don't animate), that gets worse the lower the deceleration rate is (you'll be jumping from a slow movement to a much faster one).
So to answer the question:
- no, there is no easy way to do this, it'll take some time polishing the actual values of the previous algorithm, which might not work at all on your screen,
- don't try to create your own scroll view, you'll just waste time and badly reinvent a beautiful piece of engineering apple created with truck loads of bug. The scrollview delegate is the key to your problem.
Try something like this:
- (void) snapScroll;
{
int temp = (theScrollView.contentOffset.x+halfOfASubviewsWidth) / widthOfSubview;
theScrollView.contentOffset = CGPointMake(temp*widthOfSubview , 0);
}
- (void) scrollViewDidEndDragging:(UIScrollView *)scrollView willDecelerate:(BOOL)decelerate;
{
if (!decelerate) {
[self snapScroll];
}
}
- (void) scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
[self snapScroll];
}
This takes advantage of the int's drop of the post-decimal digits. Also assumes all your views are lined up from 0,0 and only the contentOffset is what makes it show up in different areas.
Note: hook up the delegate and this works perfectly fine. You're getting a modified version - mine just has the actual constants lol. I renamed the variables so you can read it easy

How to generate an end screen when two images collide?

how to generate an end screen when two images collide. I am making an app with a stickman you move with a very sensitive acceremeter. SO if it hits these spikes, (UIImages) it will generate the end screen. How do I make the app detect this collision and then generate an end screen.
I'm sure you know the rect of the two images because you need to draw them so you can use
bool CGRectIntersectsRect (
CGRect rect1,
CGRect rect2
);
It returns YES if the two rects have a shared point
The fact that you haven't declared any rects doesn't matter. You need rects for collision detection. I assume that you at least have x and y coordinates for the stickman and you should have some kind of idea of his height and width. Judging from the question title it seems like you're using images to draw the objects you want to check for collision, so you should know the height and width of the images you're using. If you don't have this info you can't draw the objects in the right place and you certainly can't check for collisions.
You basically want to use the same rects that you use for drawing the objects.
Some code examples:
If your coordinates point to the middle of the stickman you would use something like the following:
if (CGRectIntersectsRect(CGRectMake(stickman.x-stickman.width/2,
stickman.y-stickman.height/2,
stickman.width,
stickman.height),
CGRectMake(spikes.x-spikes.width/2,
spikes.y-spikes.height/2,
spikes.width,
spikes.height))) {
// Do whatever it is you need to do. For instance:
[self showEndScreen];
}
If your coordinates point to the top left corner of your stickman you would use:
if (CGRectIntersectsRect(CGRectMake(stickman.x,
stickman.y,
stickman.width,
stickman.height),
CGRectMake(spikes.x,
spikes.y,
spikes.width,
spikes.height))) {
// Do whatever it is you need to do. For instance:
[self showEndScreen];
}
If I might give you a suggestion, I would suggest storing the coordinates and sizes in a CGRect, so that you don't have to create a new CGRect every time you're checking for collision.