Find the position of subview in a View - iphone

Goal : How we can find the position of subView in a View. I wants to find out the upper left (x,y)position of my scrollBar in a View, I am also using navigation Bar in my Application does it effect on the positioning of subViews from the Top?
I know how to find the height and width of subview like
CGSize viewSize = scrollView.frame.size;
height=viewSize.height;
width=viewSize.width;

What you're looking for is probably:
CGPoint viewPosition = scrollView.frame.origin;
x=viewPosition.x;
y=viewPosition.y;
But if you're looking to translate to another view's coordinate system, you could use:
- (CGPoint)convertPoint:(CGPoint)point toView:(UIView *)view
And pass it the point and view you wish to translate.

To get the x and y use origin
CGPoint point = scrollView.frame.origin;
float x = point.x;
float y = point.y;

You can use this one too...
CGRect rect = [scrollBar frame];
float origineFromX = rect.origin.x;
float origineFromY = rect.origin.y;
may this will help you ...

If you put navigation bar in the view, the position of the scrollview has to be shifted downwards to align properly in the view. The origin(top left position) of the scrollview will be different with and without the Navigation bar.
If you are using a fullscreen scrollview, you have to reduce the height of the scrollview to accomodate the navigation bar.

Related

UIView positioning related questions (iOS)

I have a bunch of questions:
How do I position a UIView so that it is on the bottom of the view I am adding it to?
How can I add a subview to a view so that it is positioned in the corner of the superview with a small gap (Like if I want a 'x' cross sign for closing something)
Is their a utility class for easy UIView positioning (and rotation)?
Any references, open source tutorials etc. will be more then welcome!
(a) How do I position a UIView so that it is on the bottom of the view I am adding it to?
OK, let's say you want to position button as a subview at the bottom of view form, you calculate the origin.y of the subview button by subtracting button's height from the height of the form
CGRect buttonFrame = button.frame;
buttonFrame.origin.y = form.bounds.size.height - buttonFrame.size.height;
button.frame = buttonFrame;
[form addSubview:button];
You can change origin horizontal position as well. You want it on the bottom left of form?
buttonFrame.origin.x = 0;
Or on the right edge of form?
buttonFrame.origin.x = form.bounds.size.width - buttonFrame.size.width;
Or in the middle (horizontally) of form?
buttonFrame.origin.x = (form.bounds.size.width - buttonFrame.size.width) / 2;
or another way using CGRectGetMidX (found in CGGeometry utility methods):
buttonFrame.origin.x = CGRectGetMidX(form.bounds) - buttonFrame.size.width/2;
Autoresizing handles adjusting the frame when the parent view's size changes. But you still have to position it first.
int xOffset = 20;
int yOffset = 20;
CGRect BottomRight_NewFrame = CGRectMake((superview.frame.size.width - subview.frame.size.width-xOffset), (superview.frame.size.height - subview.frame.size.height-yOffset), subview.frame.size.width, subview.frame.size.height);
subview.frame = BottomFrame;
You can use the new Autolayout feature of iOS 6 or the old Struts & Springs in the Interface Builder to achieve this.
This tutorial explains both:
http://msmvps.com/blogs/kevinmcneish/archive/2012/12/10/tutorial-ios-6-auto-layout-versus-springs-and-struts.aspx
Or you can set the autoresizing mask programatically. It is explained pretty well here:
UIView autoresizingMask - Interface Builder to Code - Programmatically create struts and springs - Swift or Objective-C
It's easy enough to just set the frame, e.g. (untested code )
subview.frame = CGRectMake((superview.frame.origin.x - subview.frame.origin.size.width/2)-20, (superview.view.frame.origin.y - subview.frame.origin.size.height/2)-20, subview.view.frame.size.width, subview.view.frame.size.height);
if you'll be doing a lot of this then create a utility class or method.
Autolayout will help you position the views and maintain those positions if the size of the superview changes. If the superview isn't going to change, you don't really need to mess with constraints -- you can just set the position of the subview appropra
If you're adding view viewB to view viewA:
a) To position viewB so that it's bottom edge corresponds to the bottom edge of viewA:
viewB.frame.origin.y = viewA.bounds.size.height - viewB.bounds.size.height;
b) You don't say which corner, but it's just a matter of doing the math. For example, the upper right corner of viewA is at {viewA.bounds.size.x, 0} in viewA's coordinate system. If you want to put viewB there, set it's origin to:
{viewA.bounds.size.x-viewB.bounds.size.x, 0}
If you want to add a margin, you can add that to the computation:
int margin = 10;
{viewA.bounds.size.x-viewB.bounds.size.x-margin, margin}
d) Use NSLayoutConstraint to access the autolayout system's constraints programmatically. There's a nice visual format language, so that for your question (a) you could set the constraint for viewA to:
V:|-[viewB]-0-|
The V means that you're working in the vertical direction, |'s represent the edges (top and bottom, thanks to the V) of the superview (that's viewA), and the 0 means that the distance between viewB and the bottom of its superview should be 0.
You can setup constraints in iOS6 but if you want to work on older os's you need to position them manually. Math.

How to rotate a CGRect or CGImageCreateWithImageInRect?

To describe my project:
I have a rectangle UIImageView frame floating over a white layer. Inside the UIImageView, I'm successfully creating the illusion that it is showing a portion of a background image behind the white layer. You can drag the rectangle around, and it will "redraw" the image so that you can peer into what is behind the white. Its basically this code:
//whenever the frame is moved, updated the CGRect frameRect and run this:
self.newCroppedImage = CGImageCreateWithImageInRect([bgImage.image CGImage], frameRect);
frame.image = [UIImage imageWithCGImage:self.newCroppedImage];
Anyhow, I also have a rotation gesture recognizer that allows the user to rotate the frame (and consequentially rotates the image). This is because the CGRect sent to the CGImageCreateWithImageInRect is still oriented at its original rotation. This breaks the illusion that you're looking through a window because the image you see is rotated when only the frame should appear that way.
So ideally, I need to take the rotation of my frame and apply it to the image created from my bgImage. Does anyone have any clues or ideas on how I could apply this?
I suggest you take a different approach. Don't constantly create new images to put in your UIImageView. Instead, set up your view hierarchy like this:
White view
"Hole" view (just a regular UIView)
Image view
That is, the white view has the hole view as a subview. The hole view has the UIImageView as its subview.
The hole view must have its clipsToBounds property set to YES (you can set it with code or in your nib).
The image view should have its size set to the size of its image. This will of course be larger than the size of the hole view.
And this is very very important: the image view's center must be set to the hole view's center.
Here's the code I used in my test project to set things up. The white view is self.view. I start with the hole centered in the white view, and I set the image view's center to the hole view's center.
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:YES];
CGRect bounds = self.view.bounds;
self.holeView.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
self.holeView.clipsToBounds = YES;
bounds = self.holeView.bounds;
self.imageView.center = CGPointMake(CGRectGetMidX(bounds), CGRectGetMidY(bounds));
self.imageView.bounds = (CGRect){ CGPointZero, self.imageView.image.size };
}
I also set the image view's size to the size of its image. You might want to set it to the size of the white view.
To pan and rotate the hole, I'm going to set holeView.transform. I'm not going to change holeView.frame or holeView.center. I have two instance variables, _holeOffset and _holeRotation, that I use to compute the transform. The trick to making it seem like a hole through the white view, revealing the image view, is to apply the inverse transform to the image view, undoing the effects of the hole view's transform:
- (void)updateTransforms {
CGAffineTransform holeTransform = CGAffineTransformIdentity;
holeTransform = CGAffineTransformTranslate(holeTransform, _holeOffset.x, _holeOffset.y);
holeTransform = CGAffineTransformRotate(holeTransform, _holeRotation);
self.holeView.transform = holeTransform;
self.imageView.transform = CGAffineTransformInvert(holeTransform);
}
This trick of using the inverse transform on the subview only works if the center of the subview is at the center of its superview. (Technically the anchor points have to line up, but by default the anchor point of a view is its center.)
I put a UIPanGestureRecognizer on holeView. I configured it to send panGesture: to my view controller:
- (IBAction)panGesture:(UIPanGestureRecognizer *)sender {
CGPoint offset = [sender translationInView:self.view];
[sender setTranslation:CGPointZero inView:self.view];
_holeOffset.x += offset.x;
_holeOffset.y += offset.y;
[self updateTransforms];
}
I also put a UIRotationGestureRecognizer on holeView. I configured it to send rotationGesture: to my view controller:
- (IBAction)rotationGesture:(UIRotationGestureRecognizer *)sender {
_holeRotation += sender.rotation;
sender.rotation = 0;
[self updateTransforms];
}

How do you zoom a UIScrollView's content to fit

My UIScrollView on the iPhone is 480x230 and the content is 972x230 when first displayed. I need to provide functionality where when the user double taps the UIScrollView the contents zoom to fit with the 480x230 UIScrollView proportionally. When the double tap it again it should zoom back out to it's original size.
What is the best way to do this? I have been fumbling for several hours with this and thought that this would work...
[bodyClockScrollView zoomToRect:bodyClockScrollView.frame animated:YES];
But nothing seems to happen.
Thanks, any help pointing me in the right direction would be appreciated.
You need to make sure the class you've set up as the scrollview's delegate implements this method:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView {
return someView;
}
Using CGAffineTransform I figured out a way to do what I want...
//bodyCleckScrollView contains a UIView name bodyClock
//get the scale factor required to fit bodyClock inside the iPhone window and resize it...
CGFloat scale = 480/bodyClockScrollView.contentSize.width;
bodyClock.transform = CGAffineTransformScale(bodyClock.transform, scale, scale);
//move bodyClock to the bottom of bodyScrollView.
bodyClock.transform = CGAffineTransformTranslate(bodyClock.transform, 0.0,bodyClockScrollView.frame.size.height-bodyClock.frame.size.height);
//scoll bodyScrollView so that bodyClock is centered in the window
CGPoint offsetPoint = CGPointMake(bodyClock.frame.origin.x, 0.0);
[bodyClockScrollView setContentOffset:offsetPoint animated:YES];
This works great and when I want to zoom it back out to the default size and position you simply call...
bodyClock.transform = CGAffineTransformIdentity;

iPhone CGRect - Get Frame Size?

I'm looking to find out the "lower limit" of a frame. The frame in question is a UITextView that resizes based on the size of the content e.g.
CGRect frame = mainTextBox.frame;
frame.size.height = mainTextBox.contentSize.height;
mainTextBox.frame = frame;
This works fine but it is the last item on a scrollView. So what I want to do is program the scroll view to only be as long as nessisary e.g.
[scrollView setContentSize:CGSizeMake(320,1850)]
What I want to do is make the 1850 the right size so that the scrollView finishes just below the mainTextBox.
Is there anyway to get a return as to the distance of the bottom of the frame to the top of the view?
Just add the y coordinate of the origin point (upper left corner of the view) to the height.
CGFloat lowerBound = mainTextBox.frame.origin.y + mainTextBox.frame.size.height;
would CGRectGetMaxY(mainTextBox.frame); work for your needs?

iPhone SDK: Setting the size of UISearchDisplayController's table view

My app's table view does not occupy the full screen height, as I've allowed 50px at the bottom for a banner.
When I begin typing in the search bar, the search results table view is larger; it fills all available screen space between the search bar and the tab bar. This means that the very last search result is obscured by the banner.
How do I specify the size of the table view used by UISearchDisplayController? There's no bounds or frame property that I can see.
EDIT TO ADD SCREENSHOTS:
This is how the table view is set up in IB. It ends 50px short of the synthesized tab bar.
(source: lightwood.net)
This is how content displays normally. I've scrolled to the very bottom here.
(source: lightwood.net)
This is how it displays when searching. Again, I've scrolled to the very bottom. If I disable the banner ad, I can see that the search display table spreads right down to the tab bar.
(source: lightwood.net)
The key to solving this one was finding out when to change the geometry of the table view. Calling:
[self.searchDisplayController.searchResultsTableView setFrame:someframe];
after creating the UISearchDisplayController was futile. The answer was this delegate method:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
tableView.frame = someframe;
}
Note, I had also tried -searchDisplayController:didLoadSearchResultsTableView but it did no good in there. You have to wait until it's displayed to resize it.
Also note that if you simply assign tableView.frame = otherTableView.frame, the search results table overlaps its corresponding search bar, so it is impossible to clear or cancel the search!
My final code looked like this:
-(void)searchDisplayController:(UISearchDisplayController *)controller didShowSearchResultsTableView:(UITableView *)tableView {
CGRect f = self.masterTableView.frame; // The tableView the search replaces
CGRect s = self.searchDisplayController.searchBar.frame;
CGRect newFrame = CGRectMake(f.origin.x,
f.origin.y + s.size.height,
f.size.width,
f.size.height - s.size.height);
tableView.frame = newFrame;
}
I updated the code to allow for deeper view hierarchies, but the initial frame of the semi-transparent cover view still takes up the entire window below the search bar.
-(void)searchDisplayController: (UISearchDisplayController*)controller
didShowSearchResultsTableView: (UITableView*)tableView
{
if ( [controller.searchBar.superview isKindOfClass: [UITableView class]] )
{
UITableView* staticTableView = (UITableView*)controller.searchBar.superview;
CGRect f = [tableView.superview convertRect: staticTableView.frame fromView: staticTableView.superview];
CGRect s = controller.searchBar.frame;
CGRect newFrame = CGRectMake(f.origin.x,
f.origin.y + s.size.height,
f.size.width,
f.size.height - s.size.height);
tableView.frame = newFrame;
}
}
I'm not sure I completely understand what you're describing, it would be nice to have a screenshot.
It sounds like what's happening is the UITableView is the size of the screen and the banner is overlapping the bottom 50 pixels of it. All UIView children have a frame, bounds, and center properties they inherit from their common UIView parent.
#interface UIView(UIViewGeometry)
// animatable. do not use frame if view is transformed since it will not correctly reflect the actual location of the view. use bounds + center instead.
#property(nonatomic) CGRect frame;
// use bounds/center and not frame if non-identity transform. if bounds dimension is odd, center may be have fractional part
#property(nonatomic) CGRect bounds; // default bounds is zero origin, frame size. animatable
#property(nonatomic) CGPoint center; // center is center of frame. animatable
#property(nonatomic) CGAffineTransform transform; // default is CGAffineTransformIdentity. animatable
// ... from UIView.h
You can manipulate these properties as you like, it sounds like you simply need to adjust the bounds to be 50 pixels smaller than the screen, and do some math to calculate your new center.