iPhone UIView center property returning bad value - objective-c

I'm working with a View-based application compiling for iPhoneOS 4.0 Simulator (Debug), Xcode 3.2.3.
I've got a UIImageView, imgView, whose center I want the coordinates of. I obtain them like this:
CGPoint imgviewcoords=[imgView center];
This doesn't produce any compile-time errors, but when I NSLog the coordinates like this:
NSLog(#"x: %i, y:%i", imgviewcoords.x, imgviewcoords.y);
I get this output:
x: 0, y:108762
It's showing 0 for imgView's x coordinate (which I know isn't right, because imgView is near the top-middle of the screen on Interface Builder) and some giant impossible number which is way past the boundaries of the iPhone's screen for the y coordinate (The y coordinate in the output above may not be exactly correct, but it's some giant number like that). I get this same exact output each time. The imgView is properly linked to its File's Owner outlet, and I can even change its image using
[imgview setImage:[UIImage imageNamed:#"./blahblah.png"]];
I just can't seem to properly get its center coordinates.
I've also tried
CGPoint viewcoords=[[imgView frame] origin];
and that gives me the same erroneous coordinates in imgviewcoords as described above.
This happens with every control that I have in my app's main UIView, except the y coordinate differs a little bit for each control.
What am I doing wrong?
#Vladimir : Thanks for the suggestion to change the NSLog format specifiers. However, I don't think it's the output that is the problem. I think it's the [imgView center] call that isn't working. I'm using the CGPoint that's returned from [imgView Center] to set the center of another UIImageView, and that UIImageView simply moves to the very top-left of the screen instead of moving to the center of imgView. So I'm guessing it's the [imgView center] call that is returning a bad set of coordinates.

%i format specifier expects integer value and CGPoint components have CGFloat type, try to use correct specifier (%f) - may be you will get correct output:
NSLog(#"x: %f, y:%f", imgviewcoords.x, imgviewcoords.y);

Related

Constraints in iOS 8

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.

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

Place sprite in relation to point on screen, not on map

In Cocos2D, I would like a sprite placed on a screen coordinate, not a map coordinate. I thought I could get by using convertToNodeSpace, but it doesn't seem to do what I want.
I thought this should place a sprite in the middle of my iPad screen:
selectionScreenOverlaySprite.position = [self convertToNodeSpace:CGPointMake(512, 384)];
But it doesn't. It also places it in a different place depending on the size of my map. Does anyone know what I should be using? I've also tried: convertToWorldSpace, convertToNodeSpaceAR, and convertToWorldSpaceAR.
Try this:
CGSize wins = [[CCDirector sharedDirector] winSize];
[yourSprite setPosition:CGPointMake(wins.width / 2, wins.height / 2)];
This is better than using hard-coded values because it will work regardless of resolution.

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.