How to center a subview - ios

I am adding a UIViewController subView to another UIViewController.
It works great. But I am having a hard time trying to center the subview in the middle of the parent view.
I read up and I found 2 lines of code that worked for other people but its not working for me.
Could anyone point out my problem??
those are:
popupController.view.center = [self.view convertPoint:self.view.center fromView:self.view.superview];
and
popupController.view.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
Parent View Controller code:
- (IBAction)selectRoutine:(id)sender {
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
createRoutinePopupViewController* popupController = [storyboard instantiateViewControllerWithIdentifier:#"createRoutinePopupView"];
// popupController.view.center = [self.view convertPoint:self.view.center fromView:self.view.superview];
popupController.view.center = CGPointMake(self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
//Tell the operating system the CreateRoutine view controller
//is becoming a child:
[self addChildViewController:popupController];
//add the target frame to self's view:
[self.view addSubview:popupController.view];
//Tell the operating system the view controller has moved:
[popupController didMoveToParentViewController:self];
}
child settings

Seems like you reposition your view controllers view after centering it. Probably in didMoveToParentViewController:.
Move the centering code to the end of selectRoutine: method
- (IBAction)selectRoutine:(id)sender
{
UIStoryboard *storyboard = [UIStoryboard storyboardWithName:#"Storyboard" bundle:nil];
createRoutinePopupViewController* popupController = [storyboard instantiateViewControllerWithIdentifier:#"createRoutinePopupView"];
[self addChildViewController:popupController];
[self.view addSubview:popupController.view];
[popupController didMoveToParentViewController:self];
//do the centering here
popupController.view.center = [self.view convertPoint:self.view.center fromView:self.view.superview];
}
Or better yet, move it to the didMoveToParentViewController:
- (void)didMoveToParentViewController:(UIViewController *)parent
{
//whatever code you have here
self.view.center = self.view.superview.center;
}
Possibly you will have to modify this code a bit. But i'm certain that your problem is incorrect (execution-time-wise) placement of the centering code - that gets subsequently overriden by some other view-positioning.

For moving a subview to the center of view i tried this
YourView.center = CGPointMake(self.view.center.x,self.view.center.y);

The solutions posted here works for me. In general if you want the spinner to come between the UIAlertController message (which generally is at the center of the UIAlertController) and any action buttons below (like Ok or cancel), slightly adjust it's height like
spinnerIndicator.center = CGPointMake(self.superview.view.bounds.width / 2,
(self.alertVC.view.bounds.height / 2) * 1.07)
// The superview of the spinnerIndictator here is the UIAlertController.
The ratio 1.07 positions the spinner just between the message and action buttons beneath for all available iPhone screens based on my experimentation.

Related

iOS convertPoint:toView: return inconsistent values

I'm trying to get the offset of my viewControllers' views relative to the top of the screen.
I thought I could convert the origin of the view to the window's coordinates so I tried something like this in my viewControllers:
CGPoint basePoint = [self.view convertPoint:self.view.frame.origin toView:nil];
CGFloat offset = basePoint.y;
It works as expected in some cases, however in other cases it returns different values even when the parameters are the same.
Any ideas on what's going on behind the scenes of this convertPoint:toView: that might be resulting in different return values?
Or if you have any other suggestions to get the offset of my viewControllers' views relative to the top of the screen it would be very much appreciated!
Thanks
You are converting your point to a point in a nil view.
The initial window, has a rootViewController which has a view and so you can access to that view:
UIView *firstView = [[[(YourClassAppDelegate *)[UIApplication sharedApplication] window] rootViewController] view];
CGPoint basePoint = [self.view convertPoint:self.view.frame.origin toView:firstView];
CGFloat offset = basePoint.y;
remember to import in the implementation, your class of the app delegate.
I end up crawling up the chain of superviews and adding up all the offsets.y from each view. Not sure if it's the best approach, especially performance wise, but for now it works.
UIView *view = self.view;
CGFloat offset = view.frame.origin.y;
while (view.superview != nil) {
offset += view.superview.frame.origin.y;
view = view.superview;
}
try change this
CGPoint basePoint = [self.view convertPoint:self.view.frame.origin toView:nil];
to
CGPoint basePoint = [self.view.superview convertPoint:self.view.frame.origin toView:nil];
Your two views must have common superview (for example UIWindow), otherwise you'll get weird results.
This might happen when you try to use UIViewController's view in -viewDidLoad with convertPoint, because view is not yet added to the view hierarchy.
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.view.superview); // nil, not added to view hierachy
// If you try to convert point using self.view (or any self.view subview),
// and any other view, which is not self.view subview (for example, UIWindow),
// it will fail, because self.view is not yet added to the view hierachy.
}

UIPageViewController & Touch events

I have followed the following tutorial to implement my collection view here
There is a back button implemented in the root view; however it does not receive any touch events as they are intercepted by the pageContentViewController.
From stackoverflow I have found the current thread of discussions which deal with a similar topic here. There is the following solution:
for (UIGestureRecognizer *gR in self.view.gestureRecognizers) {
gR.delegate = self;
}
However, it does not work as there is no gestureRecognizers as my pageViewController is in scroll mode; not curl. I cannot find any other solutions which do not point towards the code above.
What would be then the approach for the scroll mode? Any hint is highly appreciated!
The code was pretty similar to the one mentioned in the tutorial here
I've finally come around the problem by creating a tap gesture in the view of the pagecontentviewcontroller. The main idea is to capture the tap event, gets its tap location and compare it with the boundaries of the back button outlet of the parentviewcontroller as below:
- (IBAction)TapGestureOnPageContentView:(id)sender {
// get the tapped point from the sender
CGPoint tapPoint = [sender locationInView:self.view];
// retrieve the dimension from the back button; outlet reference imported from RootViewController
//
CGRect rectangle = backButton.bounds;
CGPoint center = backButton.center;
int width= rectangle.size.width /2;
int height = rectangle.size.height /2;
if ((tapPoint.x >= (center.x - width))
&& (tapPoint.x <= (center.x + width))
&&(tapPoint.y >= (center.y - height))
&& (tapPoint.y <= (center.y + height))
)
{
UIStoryboard* mainStoryBoard = [UIStoryboard storyboardWithName:#"MainStoryboard_iPhone" bundle:nil];
WizzMainScreenController* vc = [mainStoryBoard instantiateViewControllerWithIdentifier:#"WizzMainScreen"];
[vc setCentralUIManager:centralUIManager];
[self presentViewController:vc animated:YES completion:nil];
}
}
The code above works fine for me, hope it helps you too!

resizing the second child view controllers view in iPad

I am doing a PhoneGap iPad application. In this I had added a HybridPage (which contains webView) as a child view controller and then after that screen I am adding a New Controller (also removed the hybridPage from ParentViewController) which contains Native Controller and again same instance of HybridPage Controller (added two controllers as childs).
Here My Native Controller size is (320,704) and Hybrid Controller size is (702,704).
Now the problem is I am not able to interact with Half frame of Hybrid controller from right side (320 width from the end of contentoffset of the view).
I have given the two viewControllers frames even though I am not able to interact with some rect of Hybrid controller.
(void)setDashboardRootViewController:(UIViewController*)rootViewController
{
if ([[self childViewControllers] containsObject:[MCIPadRootViewController getIPadRootViewController].mcWebViewController])
{
[[MCIPadRootViewController getIPadRootViewController] removeMCWebViewController];
}
[self addChildViewController:self.dashboardViewController];
[self.dashboardContainerView addSubview:self.dashboardViewController.view];
if ([rootViewController isKindOfClass:[MCLoginFlowWebViewController class]])
{
[self addChildViewController:[MCIPadRootViewController getIPadRootViewController].mcWebViewController];
[MCIPadRootViewController getIPadRootViewController].mcWebViewController.view.frame = CGRectMake(0, 0, self.mcWebViewContainerView.frame.size.width, self.mcWebViewContainerView.frame.size.height);
[self.mcWebViewContainerView addSubview:[MCIPadRootViewController getIPadRootViewController].mcWebViewController.view];
}
}
Where can i set Child ViewController's Frame. Here i had set the viewcontroller's view frame but it is not effecting.
Could anybody help me to get out from this issue.
Please find the Screen shot for clarity.
You would typically set the frame of a child view controller's view in 2 places.
Right before you add it to the parent. Search for a call to addChildViewController: in the parent view controller. Set frame right before that call (there may be 2 calls to addChildViewController:).
If the size changes due to rotation you'll need to reset the frame of the child view controller's view in the parent view controller's viewWillLayoutSubviews: method.
Here's an example from a custom split view controller.
Notice the 2 places where where #1 is set.
- (void)setMasterViewController:(UIViewController *)controller
{
if (!_masterViewController) {
// Add the new controller
controller.view.frame = self.masterView.bounds;
[self addChildViewController:controller];
[self.masterView addSubview:controller.view];
[controller didMoveToParentViewController:self];
_masterViewController = controller;
}
else if (_masterViewController != controller) {
// Transition from the old to new controller
controller.view.frame = self.masterView.bounds;
[_masterViewController willMoveToParentViewController:nil];
[self addChildViewController:controller];
self.view.userInteractionEnabled = NO;
[self transitionFromViewController:_masterViewController
toViewController:controller
duration:0.0
options:UIViewAnimationOptionTransitionNone
animations:^{}
completion:^(BOOL finished) {
self.view.userInteractionEnabled = YES;
[_masterViewController removeFromParentViewController];
[controller didMoveToParentViewController:self];
_masterViewController = controller;
}
];
}
}
Here is where #2 is set:
- (void)viewWillLayoutSubviews
{
CGRect contentFrame = self.view.bounds;
float contentWidth = contentFrame.size.width;
CGRect masterRect = contentFrame;
CGRect detailRect = contentFrame;
CGRect dividerRect = contentFrame;
if (UIInterfaceOrientationIsLandscape(self.interfaceOrientation)) {
masterRect.size.width = DEFAULT_SPLIT_POSITION;
detailRect.origin.x = DEFAULT_SPLIT_POSITION + DEFAULT_DIVIDER_WIDTH;
detailRect.size.width = contentWidth - (DEFAULT_SPLIT_POSITION + DEFAULT_DIVIDER_WIDTH);
dividerRect.size.width = DEFAULT_DIVIDER_WIDTH;
dividerRect.origin.x = DEFAULT_SPLIT_POSITION;
}
else {
masterRect.size.width = contentWidth;
detailRect.origin.x = contentWidth;
dividerRect.origin.x = contentWidth;
}
self.masterView.frame = masterRect;
self.detailView.frame = detailRect;
self.dividerView.frame = dividerRect;
}
[Edited]
So can you update your code with the following so that you call all of the view container containment methods are used and called in the correct order.
[MCIPadRootViewController getIPadRootViewController].mcWebViewController.view.frame = CGRectMake(0, 0, self.mcWebViewContainerView.frame.size.width, self.mcWebViewContainerView.frame.size.height);
[self addChildViewController:[MCIPadRootViewController getIPadRootViewController].mcWebViewController];
[self.mcWebViewContainerView addSubview:[MCIPadRootViewController getIPadRootViewController].mcWebViewController.view];
[controller didMoveToParentViewController:self];
Also I don't think your CGRectMake is correct. You normally wouldn't reference the view's frame, you would reference the parent view's bounds. Try setting it like this and see if it helps. That will have it fill the parent view but you can also tweak it.
[MCIPadRootViewController getIPadRootViewController].mcWebViewController.view.frame = self.mcWebViewContainerView.bounds;
I came up with my Own solution.
The problem is my application opens in Landscape mode So i converted my view frame to bounds.
[[[self recurringListController] view] setFrame:[[self view] bounds]];
Using the above method I overcome my issue.

How does UIActionSheet showInView method insert its view?

Im trying to implement my own CustomUIActionSheet.
I have it almost working, but I have no idea how does the showInView method works.
(void)showInView:(UIView *)view
giving a view, this method is capable of put its view in front of every single view (adding it to the windows maybe?) but its also capable of settings the rotation accordingly to the view in which is being added.
Ive tried adding it to the windows of the view that I recieve as a parameter.
CGFloat startPosition = view.window.bounds.origin.y + view.window.bounds.size.height;
self.frame = CGRectMake(0, startPosition, view.window.bounds.size.width, [self calculateSheetHeight]);
[view.window addSubview:self];
self.blackOutView = [self buildBlackOutViewWithFrame:view.window.bounds];
[view.window insertSubview:self.blackOutView belowSubview:self];
By doing this, all works, except that when I present the action sheet in landscape, the action sheet apears from the right (since the windows system reference its always the same)
I´ve also tried to add it to the rootViewController of the view windows like that:
UIView * view = view.window.rootViewController.view;
CGFloat startPosition = view.bounds.origin.y + view.bounds.size.height;
self.frame = CGRectMake(0, startPosition, view.bounds.size.width, [self calculateSheetHeight]);
[view addSubview:self];
self.blackOutView = [self buildBlackOutViewWithFrame:view.bounds];
[view insertSubview:self.blackOutView belowSubview:self];
but again, it fails in landscape, it doesnt add anything or at least, I can not see it
So, my question is, any clue of how can I add a view to the top of the hierarchy working in both orientations?
thanks a lot!
After reading some source code of custom alert views and other ui components that are dranw in the top of the hierarchy I found a working solution.
Adding the view in the first subview of the windows:
UIView * topView = [[view.window subviews] objectAtIndex:0];
So the final code is
UIView * topView = [[view.window subviews] objectAtIndex:0];
CGFloat startPosition = topView.bounds.origin.y + topView.bounds.size.height;
self.frame = CGRectMake(0, startPosition, topView.bounds.size.width, [self calculateSheetHeight]);
[topView addSubview:self];
self.blackOutView = [self buildBlackOutViewWithFrame:topView.bounds];
[topView insertSubview:self.blackOutView belowSubview:self];

Recreate the default pushViewController animation programmatically (without a navigation controller)

I want to create a View outside of the visible screen and push in it (like the default pushViewController animation), but I cannot create the UIView outside. I was trying this code here, but it doesn't work. The View gets always created and displayed in the current UIScreen bounds. That means instead of both views, the one to get pushed out and the one to get pushed in, only the view that goes out "moves", the new view just sits at it's place.
In the the .m of the view to show:
- (void)viewDidLoad
{
[super viewDidLoad];
// set the views frame off the screen
self.view.frame = CGRectMake(320, 0, 320, 460);
}
In the method that actually does the transition:
-(void)showOverview:(UIViewController *)sender //sender = mapviewController
{
NSLog(#"overview");
// current view (frame) = mapviewCOntroller.view
CGRect outFrame = self.view.frame;
if (!self.overviewViewController) {
self.overviewViewController = [[UCOverviewViewController alloc] init];
self.overviewViewController.transitionDelegate = self;
// create a new View for the overview
[self.overviewViewController.view setCenter:self.view.center];
[self.view.window addSubview:self.overviewViewController.view];
[self.view.window bringSubviewToFront:self.mapViewController.view];
}
CGRect inFrame = self.overviewViewController.view.frame;
outFrame.origin.x = outFrame.origin.x-outFrame.size.width;
inFrame.origin.x = self.view.frame.origin.x;
[UIView animateWithDuration:0.5f animations: ^{
[self.view setFrame:outFrame];
}];
}
EDIT: this is my final code, at least the important part. Now, the view thats currently visible gets slider off screen at the same time the off-screen view gets slide in.
-(void)showOverview
{
if (!self.overviewViewController) {
NSLog(#"OverviewViewController created!");
self.overviewViewController = [[UCOverviewViewController alloc] init];
self.overviewViewController.transitionDelegate = self;
// add the subView
[self.view addSubview:self.overviewViewController.view];
[self.view sendSubviewToBack:self.overviewViewController.view];
}
CGRect outFrame = self.mapViewController.view.frame;
CGRect inFrame = self.overviewViewController.view.frame;
outFrame.origin.x -= outFrame.size.width;
inFrame.origin.x = self.view.frame.origin.x;
self.isOverviewViewVisible = YES;
[UIView animateWithDuration:0.5f animations: ^{
[self.mapViewController.view setFrame:outFrame];
[self.overviewViewController.view setFrame:inFrame];
}];
}
your problem is this line.
self.overviewView = self.overviewViewController.view;
You're overriding the view you created with the view you already have.
the following is not a great way to do it but it should work with what I think you have.
- (void)viewDidLoad {
self.overviewViewController.view.frame = CGRectMake(320, 0, 320, 460);
self.overviewViewController.view.backgroundColor = [UIColor blueColor];
}
Then you want another action to do the animations.
[UIView animateWithDuration:0.5f animations: ^{
self.overviewViewController.view.frame = CGRectMake(0,0,320,460);
}];
There are weird and incorrect things in your code that make it confusing to understand what is actually going on, but... forget about using the window anywhere. The only thing you need to worry about, is this. Say View A is your main view. View B is the view you want outside and to come in over A. Then, create B either from nib or manually, add it as a subview to view A, and before you enter the animation block to move it into place, make sure that the current frame is set to {ViewA.bounds.size.width, 0, ViewB.bounds.size.width, ViewB.bounds.size.height}. Assuming you want it to come in from the top right.

Resources