I have a very strange problem. I trying to rotate and move a UIView's superview manually in viewDidAppear like so:
- (void)viewWillAppear:(BOOL)animated
{
// ...
[self.view.superview setTransform:CGAffineTransformMakeRotation(M_PI/2)];
[self.view.superview setCenter:CGPointMake(100, 200)]; // (100, 200) is toy value
// ...
}
For some reason, the rotation is applied correctly but the superview's center (i.e. position) is not moved at all. I've also tried setting the transform to a CGAffineTransform that's a combination of a rotation + translation (or just a translation alone), and that won't move the superview either.
If it makes a difference, self.view.superview is the top-most view (i.e. self.view.superview.superview is nil).
Am I missing something very simple here?
Edit: nielsbot's comment was correct in that putting it in viewDidLoad worked, but by then, the view has already appeared so there's a flicker where it snaps to the new location. Is there a way around this?
Try setting the bounds of the view so:
[self.view.superview setBounds:CGRectMake(x,y,self.view.superview.width,self.view.superview.height)];
This should move your view to the x,y position you give him
But of course this is not the center
The center just represents a special point that is for example used for rotations
Related
I have a UIView that I move with a pan gesture to the side of the screen and now the UIView is only partially displayed on the screen. How do I get the CGRect that contains ONLY the visible portion of the UIView, AND, in the coordinates of the original view?
I've tried combinations of CGRectIntersect() for the UIView.frame rect and the [UIScreen mainscreen].bounds rect, like this:
CGRect rect = CGRectIntersection([UIScreen mainScreen].bounds,
view.frame);
I haven't been able to correctly resolve matching the coordinate systems.
You first need to translate the CGRect of your displaced view, to the coordinate system of the main screen.
You can try something like
if let mainVC = UIApplication.shared.keyWindow?.rootViewController {
let translatedRect = mainVC.view.convert(myTestView.frame, from: view)
let intersection = translatedRect.intersection(mainVC.view.frame)
}
This first finds the main rootViewController, and translates your view's frame to rootViewController's coordinate system, and then finds the intersection. This would work even if your displaced view is nested in multiple layers of views.
After some (hours of) experimentation I came up with this solution:
// 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 frame
}
I first tried to convert the root view to the destination view's coordinates and then do the CGRectIntersect on the view frame, but it did not work. But I got it working the reverse way for UIViews with the root view as their superview. Then after some spelunking I figured out that I had to navigate thru the view hierarchy for subviews.
It works for UIViews where the superview is the root view and also for UIViews that are subviews of other views.
I tested it by drawing borders around these visible rects on the initial views, and it works perfectly.
BUT ... it does NOT work correctly if a UIView is scaled (!=1) AND a subview of another UIView other than the root view. The origin of the resultant visible rect is offset by a bit. I tried a few different ways to adjust the origin if the view is in a subview but I couldn't figure out a clean way to do it.
I've added this method to my utility UIView category, along with all the other "missing" UIView methods I've been developing or acquiring. (Erica Sadun's transform methods ...I'm not worthy...)
This does solve the problem I was working on though. So I will post another question regarding the scaling problem.
EDIT:
While working on the question and answer for the scaling issue, I came up with a better answer for this question too:
// 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
}
I have an Application in Which there is a lot of UIView's Nested together.
I want to find the frame of some UIView's with respect to Controller.
I have setup an example:
I am able to find to frame of White UIView w.r.t to Controller but how can I find the frame of RedView wrt to Controller.
Code to find the Frame of White View:
- (void)viewDidAppear:(BOOL)animated
{
NSLog(#"--OuterFrame---%#",NSStringFromCGRect(self.grayView.frame));
NSLog(#"--InnerFrame---%#",NSStringFromCGRect(self.whiteView.frame));
NSLog(#"---Frame With resepect to self.view --%#",NSStringFromCGRect([self.grayView convertRect:self.whiteView.frame toView:self.view]));
}
And What if Their is another view inside red View.
You have the right general idea. But it's easier if you use the inner view's bounds, and use self.view to convert:
// assumes innerView is a view somewhere underneath self.view
CGRect innerViewRectRelativeToController =
[self.view convertRect:innerView.bounds fromView:innerView];
This way you only need to specify the view to convert from, and the view to convert to. You don't need to think about what views are in between the two.
I'm a little confused by UIView animateWithDuration.
I have a label placed at (224,93,152,39), centred horizontally in my storyboard. When I run viewDidLoad, I want it to slide up from centre vertically and horizontally to the position I've placed in my storyboard. So intuitively, I did this:
[UIView animateWithDuration:1.0 animations:^{
self.titleLabel.frame = CGRectMake(self.titleLabel.frame.origin.x,self.view.center.y, 0, 0);
}];
This actually did the opposite for me. The label slides from above the status bar to the desired position (224,93,152,39). I want it to start from centre of the viewController to the desired position (224,93,152,39).
I think I'm not getting something essential of the UIView animations. Would appreciate it if someone can point it out for me.
May be you can try by place the above code to viewDidAppear method
The frame you set in the animations block is the ending frame. Set the starting frame right before you call animateWithDuration if needed.
Hi Im making a simple game with the basic UIKit stuff where there is an object that pops up on the screen.
I'm trying to make it so that every time the object pops up the screen will scroll to the object and center it on screen. Basically I have a UIView that has the objects that popup within it and that UIView is a subview of a UIScrollView. All of this is a subview of self.view of my ViewController. I'm using the UIScrollview to scroll through the contents of its subview with the objects in it. I'm not sure if a UIScrollView is the ideal option for what I want to do but I was looking for a way to scroll the position of the object and center it in the middle of the device. Ideally with animation.
If anyone knows of a method or maybe a formula that could help me out that would be awesome. Thanks in advance :D
Update:
I don't really have much code in regards to this except for adding the views within each other :
self.world = [[UIView alloc]initWithFrame:CGRectMake(0, 0, currentDeviceWidth*4, currentDeviceHeight*2.2)];
self.world.backgroundColor = [UIColor blueColor];
_scrollView = [[UIScrollView alloc] initWithFrame:[[self view] bounds]];
[_scrollView setContentSize:self.world.frame.size];
[_scrollView addSubview:self.world];
[[self view] addSubview:_scrollView];
but below is a simple image of what I want to accomplish. The arrows are just a representation of the possible scrolling direction.
Apple object is off the side of the screen Note: apple object is a subview of self.world and self.world is a subview of scrollview
The finish look is where now the scrollview has scroll self.world to where the apple object is in the center of the screen
Just looking to basically accomplish how a user could manually scroll the object to the center but instead do it automatically without the use of a user's touch.
I think you may need contentOffset which is a property of UIScrollView. This property controls the position of the contentView.
Now let's say, your apple is in the center of the self.world, then animated put it in the middle is like this:
[UIView animateWithDuration: 0.5//you can put any float as you want
animations:^{self.world.contentOffset = CGPointMake((_scrollview.frame.size.width - self.world.frame.size.width) / 2, (_scrollview.frame.size.height - self.world.frame.size.height) / 2);
}];
If you put your apple in any other position, you should calculate the position yourself, and set the contentOffset to the right one. I think any position other than center is much complicated, you calculation should involve the position of the apple in the self.world view.
Did you try
[_scrollView scrollRectToVisible:newView.frame animated:(BOOL)animated]
What might be tricky about what you are trying to do is positioning views such that they are always within the coordinate system of the _scroll view, e.g. x >= middle of the view & y >= middle of the view.
I have this animation block:
[UIView animateWithDuration:10 animations:^{
CGPoint centerLeft;
centerLeft.x = self.leftMapPane.center.x - 100;
centerLeft.y = self.leftMapPane.center.y;
CGRect leftMove = CGRectMake(self.leftMapPane.frame.origin.x - 100, self.leftMapPane.frame.origin.x, self.leftMapPane.frame.size.width, self.leftMapPane.frame.size.height);
[self.leftMapPane setFrame:leftMove];
[self.leftMapPane setCenter:centerLeft];
}];
but for some reason although it gets inside the animation block, it doesn't move the mapViews I have around on the screen.
It should be noted that I'm not talking about moving the map but the view that contains the map, aka the mapview.
Any thoughts?
The animation doesn't work in viewDidLoad because at that point the views have been loaded into memory but have not been necessarily added to the view hierarchy. So when you try to animate the views it won't work.
Instead, put your animation call in viewDidAppear and it should work. This way you're sure the views you're animating are in the view hierarchy. I tried this in a sample project using your animation code and it worked fine.