add sub view in center - iPad - ipad

I want to add a sub view in current view, this sub view is 300x300. When I add subview using
[self.view addSubview:md.view];
the md.view will appear at position (0,0) is there any way to add subview in center?
Thanks

You can set view's center property:
md.view.center = self.view.center;
Or you can explicetly set frame for md.view so that it will be centered as you want.

Use
CGRect bounds = self.view.bounds;
md.view.center = CGPointMake(bounds.size.width / 2, bounds.size.height / 2);
before or after that -addSubview: line.

You can specify exactly where you want the sub view to be placed within the parent view by doing so in the viewDidLoad method as follows:
- (void)viewDidLoad {
[super viewDidLoad];
SubView1Controller *subView1Controller=[[[SubView1Controller alloc] initWithNibName:#"SubView1" bundle:nil] autorelease];
CGRect r = [subView1Controller.view frame];
r.origin.x = 50;
r.origin.y = 50;
[subView1Controller.view setFrame:r];
[self.view insertSubview:subView1Controller.view atIndex:0];
}

Related

UIView bottom constraint after rotate

I have UIProgressView that i transformed (rotated) so it would be vertical, my issue is with the bottom constraint which is not appearing correctly since the view is rotated, the bottoms of UIView and UIProgressView and not exactly aligned, i tried also to change the anchor point, couldn't the bottoms to be aligned as well.
- (void)viewDidLoad {
[super viewDidLoad];
[self initFlow];
self._wProgressView = [[UIProgressView alloc] init];
[self._middleRightView addSubview:self._wProgressView];
[self._wProgressView setProgress:0.7];
CGPoint newCenter = self._wProgressView.layer.anchorPoint;
newCenter.x = 0;
newCenter.y = 1;
[self._wProgressView.layer setAnchorPoint:newCenter];
self._wProgressView.transform = CGAffineTransformRotate(self._wProgressView.transform, -0.5 *M_PI);
// [self._wProgressView.centerXAnchor constraintEqualToAnchor:self._wProgressView.superview.centerXAnchor].active = YES;
self._wProgressView.translatesAutoresizingMaskIntoConstraints = NO;
[self._wProgressView.bottomAnchor constraintEqualToAnchor:self._wImageView.bottomAnchor].active = YES;
[self._wProgressView.widthAnchor constraintEqualToAnchor:self._wProgressView.superview.heightAnchor multiplier:0.5].active = YES;
[self._wProgressView.heightAnchor constraintEqualToAnchor:self._wProgressView.superview.widthAnchor constant:0.8].active = YES;
//= self._wProgressView.superview.centerXAnchor;
}
I got it to work this way:
You should give only height, width and Center x and y constraint of UIProgressView to UIView relation
No Need to give bottom constraint of UIProgressView
this work for me I Hope this will help you...!

How do I calculate the correct CGRect origin on a scaled UIView subview?

I need to calculate the visible CGRect of a UIView subview, in the coordinates of the original view. I've got it working if the scale is 1, but if one of the superviews or the view itself is scaled (pinch), the visible CGRect origin is offset slightly.
This works when the scale of the views is 1 or the view is a subview of the root view:
// return the part of the passed view that is visible
// TODO: figure out why result origin is wrong for scaled subviews
//
- (CGRect)getVisibleRect:(UIView *)view {
// get the root view controller (and it's view is vc.view)
UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;
// get the view's frame in the root view's coordinate system
CGRect frame = [vc.view convertRect:view.frame fromView:view.superview];
// get the intersection of the root view bounds and the passed view frame
CGRect intersection = CGRectIntersection(vc.view.bounds, frame);
// adjust the intersection coordinates thru any nested views
UIView *loopView = view;
do {
intersection = [loopView convertRect:intersection fromView:loopView.superview];
loopView = loopView.superview;
} while (loopView != vc.view);
return intersection; // may be same as the original view frame
}
When a subview is scaled, the size of the resultant view is correct, but the origin is offset by a small amount. It appears that the convertRect does not calculate the origin properly for scaled subviews.
I tried adjusting the origin relative to the X/Y transform scale but I could not get the calculation correct. Perhaps someone can help?
To save time, here is a complete test ViewController.m, where a box with an X is drawn on the visible part of the views - just create a reset button in the Main.storyboard and connect it to the reset method:
//
// ViewController.m
// VisibleViewDemo
//
// Copyright © 2018 ByteSlinger. All rights reserved.
//
#import "ViewController.h"
CG_INLINE void drawLine(UIView *view,CGPoint point1,CGPoint point2, UIColor *color, NSString *layerName) {
UIBezierPath *path = [UIBezierPath bezierPath];
[path moveToPoint:point1];
[path addLineToPoint:point2];
CAShapeLayer *shapeLayer = [CAShapeLayer layer];
shapeLayer.path = [path CGPath];
shapeLayer.strokeColor = color.CGColor;
shapeLayer.lineWidth = 2.0;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.name = layerName;
[view.layer addSublayer:shapeLayer];
}
CG_INLINE void removeShapeLayers(UIView *view,NSString *layerName) {
if (view.layer.sublayers.count > 0) {
for (CALayer *layer in [view.layer.sublayers copy]) {
if ([layer.name isEqualToString:layerName]) {
[layer removeFromSuperlayer];
}
}
}
}
CG_INLINE void drawXBox(UIView *view, CGRect rect,UIColor *color) {
NSString *layerName = #"xbox";
removeShapeLayers(view, layerName);
CGPoint topLeft = CGPointMake(rect.origin.x,rect.origin.y);
CGPoint topRight = CGPointMake(rect.origin.x + rect.size.width,rect.origin.y);
CGPoint bottomLeft = CGPointMake(rect.origin.x, rect.origin.y + rect.size.height);
CGPoint bottomRight = CGPointMake(rect.origin.x + rect.size.width, rect.origin.y + rect.size.height);
drawLine(view,topLeft,topRight,color,layerName);
drawLine(view,topRight,bottomRight,color,layerName);
drawLine(view,topLeft,bottomLeft,color,layerName);
drawLine(view,bottomLeft,bottomRight,color,layerName);
drawLine(view,topLeft,bottomRight,color,layerName);
drawLine(view,topRight,bottomLeft,color,layerName);
}
#interface ViewController ()
#end
#implementation ViewController
UIView *view1;
UIView *view2;
UIView *view3;
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
CGFloat width = [UIScreen mainScreen].bounds.size.width / 2;
CGFloat height = [UIScreen mainScreen].bounds.size.height / 4;
view1 = [[UIView alloc] initWithFrame:CGRectMake(width / 2, height / 2, width, height)];
view1.backgroundColor = UIColor.yellowColor;
[self.view addSubview:view1];
[self addGestures:view1];
view2 = [[UIView alloc] initWithFrame:CGRectMake(width / 2, height / 2 + height + 16, width, height)];
view2.backgroundColor = UIColor.greenColor;
[self.view addSubview:view2];
[self addGestures:view2];
view3 = [[UIView alloc] initWithFrame:CGRectMake(10, 10, width / 2, height / 2)];
view3.backgroundColor = [UIColor.blueColor colorWithAlphaComponent:0.5];
[view1 addSubview:view3]; // this one will behave differently
[self addGestures:view3];
}
- (void)viewWillLayoutSubviews {
[super viewWillLayoutSubviews];
[self checkOnScreen:view1];
[self checkOnScreen:view2];
[self checkOnScreen:view3];
}
- (IBAction)reset:(id)sender {
view1.transform = CGAffineTransformIdentity;
view2.transform = CGAffineTransformIdentity;
view3.transform = CGAffineTransformIdentity;
[self.view setNeedsLayout];
}
- (void)addGestures:(UIView *)view {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePan:)];
[view addGestureRecognizer:panGestureRecognizer];
UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc]
initWithTarget:self action:#selector(handlePinch:)];
[view addGestureRecognizer:pinchGestureRecognizer];
}
// return the part of the passed view that is visible
- (CGRect)getVisibleRect:(UIView *)view {
// get the root view controller (and it's view is vc.view)
UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;
// get the view's frame in the root view's coordinate system
CGRect frame = [vc.view convertRect:view.frame fromView:view.superview];
// get the intersection of the root view bounds and the passed view frame
CGRect intersection = CGRectIntersection(vc.view.bounds, frame);
// adjust the intersection coordinates thru any nested views
UIView *loopView = view;
do {
intersection = [loopView convertRect:intersection fromView:loopView.superview];
loopView = loopView.superview;
} while (loopView != vc.view);
return intersection; // may be same as the original view
}
- (void)checkOnScreen:(UIView *)view {
CGRect visibleRect = [self getVisibleRect:view];
if (CGRectEqualToRect(visibleRect, CGRectNull)) {
visibleRect = CGRectZero;
}
drawXBox(view,visibleRect,UIColor.blackColor);
}
//
// Pinch (resize) an image on the ViewController View
//
- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer {
static CGAffineTransform initialTransform;
if (recognizer.state == UIGestureRecognizerStateBegan) {
[self.view bringSubviewToFront:recognizer.view];
initialTransform = recognizer.view.transform;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
} else {
recognizer.view.transform = CGAffineTransformScale(initialTransform,recognizer.scale,recognizer.scale);
[self checkOnScreen:recognizer.view];
[self.view setNeedsLayout]; // update subviews
}
}
- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer {
static CGAffineTransform initialTransform;
if (recognizer.state == UIGestureRecognizerStateBegan) {
[self.view bringSubviewToFront:recognizer.view];
initialTransform = recognizer.view.transform;
} else if (recognizer.state == UIGestureRecognizerStateEnded) {
} else {
//get the translation amount in x,y
CGPoint translation = [recognizer translationInView:recognizer.view];
recognizer.view.transform = CGAffineTransformTranslate(initialTransform,translation.x,translation.y);
[self checkOnScreen:recognizer.view];
[self.view setNeedsLayout]; // update subviews
}
}
#end
So you need to know the real visible frame of a view that is somehow derived from bounds+center+transform and calculate everything else from that, instead of the ordinary frame value. This means you'll also have to recreate convertRect:fromView: to be based on that. I always sidestepped the problem by using transform only for short animations where such calculations are not necessary. Thinking about coding such a -getVisibleRect: method makes me want to run away screaming ;)
What is a frame?
The frame property is derived from center and bounds.
Example:
center is (60,50)
bounds is (0,0,100,100)
=> frame is (10,0,100,100)
Now you change the frame to (10,20,100,100). Because the size of the view did not change, this results only in a change to the center. The new center is now (60,70).
How about transform?
Say you now transform the view, by scaling it to 50%.
=> the view has now half the size than before, while still keeping the same center. It looks like the new frame is (35,45,50,50). However the real result is:
center is still (60,50): this is expected
bounds is still (0,0,100,100): this should be expected too
frame is still (10,20,100,100): this is somewhat counterintuitive
frame is a calculated property, and it doesn't care at all about the current transform. This means that the value of the frame is meaningless whenever transform is not the identity transform. This is even documented behaviour. Apple calls the value of frame to be "undefined" in this case.
Consequences
This has the additional consequences that methods such as convertRect:fromView: do not work properly when there are non-standard transforms involved. This is because all these methods rely on either frame or bounds of views, and they break as soon as there are transforms involved.
What can be done?
Say you have three views:
view1 (no transform)
view2 (scale transform 50%)
view3 (no transform)
and you want to know the coordinates of view3 from the point of view of view1.
From the point of view of view2, view3 has frame view3.frame. Easy.
From the point of view of view1, view2 has not frame view2.frame, but the visible frame is a rectangle with size view2.bounds/2 and center view2.center.
To get this right you need some basic linear algebra (with matrix multiplications). (And don't forget the anchorPoint..)
I hope it helps..
What can be done for real?
In your question you said that there is an offset. Maybe you can just calculate the error now? The error should be something like 0.5 * (1-scale) * (bounds.size) . If you can calculate the error, you can subtract it and call it a day :)
Thanks to #Michael for putting in so much effort in his answer. It didn't solve the problem but it made me think some more and try some other things.
And voila, I tried something that I'm certain I had done before, but this time I started with my latest code. It turns out a simple solution did the trick. The builtin UIView convertRect:fromView and convertRect:toView worked as expected when used together.
I apologize to anyone that has spent time on this. I'm humbled in my foolishness and how much time I have spent on this. I must have made a mistake somewhere when I tried this before because it didn't work. But this works very well now:
// return the part of the passed view that is visible
- (CGRect)getVisibleRect:(UIView *)view {
// get the root view controller (and it's view is vc.view)
UIViewController *vc = UIApplication.sharedApplication.keyWindow.rootViewController;
// get the view's frame in the root view's coordinate system
CGRect rootRect = [vc.view convertRect:view.frame fromView:view.superview];
// get the intersection of the root view bounds and the passed view frame
CGRect rootVisible = CGRectIntersection(vc.view.bounds, rootRect);
// convert the rect back to the initial view's coordinate system
CGRect visible = [view convertRect:rootVisible fromView:vc.view];
return visible; // may be same as the original view frame
}
If someone uses the Viewcontroller.m from my question, just replace the getVisibleRect method with this one and it will work very nicely.
NOTE: I tried rotating the view and the visible rect is rotated too because I displayed it on the view itself. I guess I could reverse whatever the view rotation is on the shape layers, but that's for another day!

Trying to center a view within its superview

I am creating a category on UIView to make programmatically positioning and sizing my views easier. I want to create a method that will center a given view horizontally or vertically in its superview. So I can do something like the following:
Category
- (void)centerHorizontally {
self.center = CGPointMake(self.window.superview.center.x, self.center.y);
}
- (void)centerVertically {
self.center = CGPointMake(self.center.x, self.window.superview.center.y);
}
Use
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[v centerHorizontally];
However, this doesn't seem to be working. What is incorrect about my solution?
You need to add the view to a parent view before you can center it.
UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
[someOtherView addSubview:v];
[v centerHorizontally];
And your category is incorrect. Don't get the window involved. You need to base it on the superview's size:
- (void)centerHorizontally {
self.center = CGPointMake(self.superview.bounds.size.width / 2.0, self.center.y);
// or
self.center = CGPointMake(CGRectGetMidX(self.superview.bounds), self.center.y);
}
- (void)centerVertically {
self.center = CGPointMake(self.center.x, self.superview.bounds.size.height / 2.0);
// or
self.center = CGPointMake(self.center.x, CGRectGetMidY(self.superview.bounds));
}

UIView that follows UICollectionView indicator

I want to create a custom UIView that contains a UIImageView and a UILabel, that points to the UICollectionView scroll indicator, and scrolls with it.
i am using this code :
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
//get refrence of vertical indicator
UIImageView *verticalIndicator = ((UIImageView *)[scrollView.subviews objectAtIndex:(scrollView.subviews.count-1)]);
// get the relative position of the indicator in the main window
CGPoint p = [verticalIndicator convertPoint:verticalIndicator.bounds.origin toView:[[UIApplication sharedApplication] keyWindow]];
// set the custom view new position
CGRect indicatorFrame = self.indicatorView.frame;
indicatorFrame.origin.y = p.y;
self.indicatorView.frame = indicatorFrame;
}
But the indicatorView does not follow the indicator exactly !!
This worked for me: Please look at the attached gif. I have added the custom uiview with blue color parallel to the scrool indicator.
I tried for the table view, but this also works for the collection view too.
-(void)scrollViewDidScroll:(UIScrollView *)scrollView{
dispatch_async(dispatch_get_main_queue(), ^{
//get refrence of vertical indicator
UIImageView *verticalIndicator = ((UIImageView *)[scrollView.subviews objectAtIndex:(scrollView.subviews.count-1)]);
// get the relative position of the indicator in the main window
// CGPoint p = [verticalIndicator convertPoint:verticalIndicator.bounds.origin toView:[[UIApplication sharedApplication] keyWindow]];
CGPoint p = CGPointMake(verticalIndicator.frame.origin.x-10,
verticalIndicator.frame.origin.y - scrollView.contentOffset.y);
// set the custom view new position
CGRect indicatorFrame = CGRectMake(verticalIndicator.frame.origin.x, verticalIndicator.frame.origin.y, 10, 10);
self.indicatorView.frame = indicatorFrame;
});
}
Also maske sure you added the uiview in you view did load method.
//in viewDidLoad:
Note that I have used the sample values for the indicatorView postion.You can replace these as per your need.
indicatorView=[[UIView alloc]initWithFrame:CGRectMake( p.x, p.y , 10, 10)];
indicatorView.backgroundColor=[UIColor blueColor];
[self.tableView addSubview:indicatorView];
For me this works:
....
scrollIndicatorView = [self scrollIndicator];
[self.collectionView addSubview:scrollIndicatorView];
....
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
// percentage of the content reached * height
CGFloat yPos = (self.collectionView.contentOffset.y / (self.collectionView.contentSize.height - self.collectionView.bounds.size.height)) * self.collectionView.bounds.size.height;
yPos += self.collectionView.contentOffset.y; // add content offset becasue view is inside colelctionview which content moves
CGRect indicatorFrame = CGRectMake(self.collectionView.bounds.size.width - 10,
yPos,
10,
30);
scrollIndicatorView.frame = indicatorFrame;
}

Dismiss a custom UIView like the way in UIPopoverController

Im building a custom UIView similar to UIPopover view , simply I subclass the UIView class and build the stuff of controls and events inside .. to show this view I assign the superView through My sub Class datasource like this
if ([dataSource respondsToSelector:#selector(containerView)])
superView = [dataSource containerView];
and to show it I have a function doing that like this
- (void) showPopOverFromRect : (CGRect) rect
{
CGSize popSize = self.frame.size;
float yPoint;
if(ntPopOverDirection == NTPopOverArrowDirectionUP)
yPoint = rect.origin.y + 10;
else
yPoint = rect.origin.y - 10;
self.frame = CGRectMake(rect.origin.x - popSize.width, yPoint , popSize.width, popSize.height);
[superView addSubview:self];
}
so My Question .. how can i dismiss this view (remove it) if the user tap AnyWhere on the superView just like the UIPopOverController ?
I suggest that you create your custom UIView to fill the entire superview or the entire screen with a clear background or a radial gradient. Then inside of this you would put another UIView that has the look and feel of the popover.
This eliminates the issue of trying to capture taps and sending notifications from other views. It will be all self contained.
You can easily add a gesture recognizer inside your custom view to close the view when the user touches the clear area.
You could place a UIButton below your new UIView that is clear. When this new button is pressed, it dismisses your view and removes itself from superview.
Something like:
- (void) showPopOverFromRect : (CGRect) rect
{
CGSize popSize = self.frame.size;
float yPoint;
if(ntPopOverDirection == NTPopOverArrowDirectionUP)
yPoint = rect.origin.y + 10;
else
yPoint = rect.origin.y - 10;
self.frame = CGRectMake(rect.origin.x - popSize.width, yPoint , popSize.width, popSize.height);
UIButton *dismissButton = [UIButton buttonWithType:UIButtonTypeCustom];
dismissButton.backgroundColor = [UIColor clearColor];
dismissButton.frame = [[UIScreen mainScreen] bounds];
[dismissButton addTarget:self.delegate action:#selector(dismissPopover) forControlEvents:UIControlEventTouchUpInside];
[superview addSubview:dismissButton];
[superView addSubview:self];
}
You'd have to set up the view to set it's superview as a delegate that receives the message to dismiss the popover, though.

Resources