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];
Related
I'm trying to create a UIScrollView with 3 UIViewController subviews. I'm done some research and it seems that the answer to this question provides a good solution Setting up UIScrollView to swipe between 3 view controllers, but I'm not sure. I downloaded the sample code which the answerer nicely posted https://github.com/gneil90/CustomContainerViewController and my question is as follows:
BViewController *bViewController = [[BViewController alloc]init];
[self addChildViewController:bViewController];
[self.scrollView addSubview:bViewController.view];
[bViewController didMoveToParentViewController:self];
CViewController *cViewController = [[CViewController alloc]init];
CGRect frame = cViewController.view.frame;
frame.origin.x = 320;
cViewController.view.frame = frame;
[self addChildViewController:cViewController];
[self.scrollView addSubview:cViewController.view];
[cViewController didMoveToParentViewController:self];
self.scrollView.contentSize = CGSizeMake(640, self.view.frame.size.height);
self.scrollView.pagingEnabled = YES;
The code in the sample project initializes both UIViewControllers at once. Is there any way to lazy load them/would this provide any kind of performance enhancements? If 2 of my viewcontrollers download data, I wouldn't want to download this data unless the user was actually viewing these screens, but with this kind of initialization it would seems as though the user quickly viewed both of them.
You can use UIScrollView delegate method, initialize only first view controller as you are doing.
For rest of your view controller you can create property. In UIScrollView delegate method scrollViewDidScroll do following-
// Check at what index, to second VC or third VC
if (some condition, you can define this based on scroll postion) {
// Check if second view controller
if (!self.cViewController) {
cViewController = [[CViewController alloc]init];
CGRect frame = cViewController.view.frame;
frame.origin.x = 320;
cViewController.view.frame = frame;
}
// similarly for third
}
Using this view controller are initialized as they are needed.
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.
}
My universal app uses NIBs for its settings screens. I'd like to use the same NIBs for both iPhone and iPad.
Thus on iPad, I use a UIPopoverController in the MainViewController and for settings, simply display the iPhone-sized NIBs, to show what is called the SettingsViewController. The popover is sized 320x460 points.
This causes a problem, because the iPhone version draws a number of things above the status bar programmatically, and for the iPad version this is not necessary. Current situation on iPad:
As you can see, there's a big empty space above the "Settings" title. Thus what I want, is to shift the view controller up about 20 points, inside the popover:
The popover is instantiated as follows in the MainViewController:
settingsPopoverController = [[UIPopoverController alloc] initWithContentViewController:popoverNavigationController];
settingsPopoverController.popoverContentSize = CGSizeMake(320, 460);
settingsPopoverController.delegate = self;
popoverNavigationController.navigationBarHidden = YES;
In the SettingsViewController, I set the frame as follows:
- (void)viewDidLoad
{
self.contentSizeForViewInPopover = CGSizeMake(320, 460);
}
And later in the SettingsViewController, I try to create an offset as follows:
- (void)viewWillLayoutSubviews
{
// shift it up
CGRect frame = self.view.frame;
frame.origin.y = -20;
[self.view setFrame:frame];
}
This does not shift the content up a bit. How to go about?
To clarify: I want to move down the "viewport" that the popover shows.
Try to:
myPopover.autoresizingMask=UIViewAutoresizingNone;
Or:
myPopover.autoresizingMask=UIViewAutoresizingFlexibleHeight || UIViewAutoresizingFlexibleWidth;
You can also try to put your code in -(void)viewDidLayoutSubviews method.
If this answers did not help, try set popoverLayoutMargins (property) instead of setFrame: for example:
popover.popoverLayoutMargins=UIEdgeInsetsMake (
CGFloat 50, //top
CGFloat 50,//left
CGFloat 50,//bottom
CGFloat 50//right
);
I design the scrollview in interface builder like this
It looks good here. But unfortunately when I run it on emulator or device
it becomes
The content in scrollview is expand outside scrollview itself and even though outside UIView that contains this scrollView.
In my viewDidLoad (panel is the container of scrollView )
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
CGFloat adjustPanelHeight = [PTTScreenScaleUtil getAdjustHeight:self.panel.frame.size.height];
CGRect panelRect = self.panel.frame;
panelRect.size.height = adjustPanelHeight;
self.panel.frame = panelRect;
UIImage *image = [UIImage imageNamed:#"panel-background"];
UIGraphicsBeginImageContext(self.panel.frame.size);
[image drawInRect:CGRectMake(0, 0, self.panel.frame.size.width, adjustPanelHeight)];
UIImage* newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
[self.panel setBackgroundColor:[UIColor colorWithPatternImage:newImage]];
NSLog(#"scrollView Height : %f", self.scrollView.frame.size.height);
NSLog(#"scrollView contentSize Height : %f", self.scrollView.contentSize.height);
// CGRect scrollViewRect = self.scrollView.frame;
// CGRect scrollViewContentRect = self.scrollView.frame;
// NSLog(#"ScrollView Height Before : %f , After : %f", self.scrollView.frame.size.height, [PTTScreenScaleUtil getAdjustHeight:self.scrollView.frame.size.height]);
// scrollViewRect.size.width = 280;
// scrollViewRect.size.height = [PTTScreenScaleUtil getAdjustHeight:270];
// self.scrollView.frame = scrollViewRect;
// [self.detailsLabel sizeToFit];
UIView *view = [[self.scrollView subviews] objectAtIndex:0];
// [view sizeToFit];
// [self.scrollView setContentMode:UIViewContentModeScaleAspectFit];
// NSLog(#"ContentSize Height : %f", view.frame.size.height);
// scrollViewContentRect.size.height = view.frame.size.height;
NSLog(#"Bounds : %f", view.bounds.size.height);
self.scrollView.frame = CGRectMake(10, 10, 280, 270);
self.scrollView.contentSize = CGSizeMake(280, 500);
NSLog(#"Frame Height %f", self.scrollView.frame.size.height);
//[self.scrollView setContentSize: CGSizeMake(280, 1000)];
CGRect termBtnRect = self.termBtn.frame;
CGRect mailBtnRect = self.mailBtn.frame;
CGRect twitterBtnRect = self.twitterBtn.frame;
CGRect fbBtnRect = self.fbBtn.frame;
termBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
mailBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
twitterBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
fbBtnRect.origin.y = adjustPanelHeight - 10 - termBtnRect.size.height;
self.termBtn.frame = termBtnRect;
self.mailBtn.frame = mailBtnRect;
self.twitterBtn.frame = twitterBtnRect;
self.fbBtn.frame = fbBtnRect;
}
All the log return 270.0
PS. the scroll bar is correct even though the content goes outside but the scroll bar is working correctly (stay in the scrollview's frame as arrange in interface builder)
I have no idea how can I solve this.
Anyone help me please.
Thanks you.
Solve it by creating new view controller in interface builder and redo the same process with careful and bingo. It works.
When I compare both two view controller I realise that the wrong one UIScrollView Clip Subviews is unchecked. When check it the problem solve.
I just struggled with this for an hour and had a head smack moment.
In my case, I had a UIView on the scene in the Storyboard. At some point I decided I needed it to be a UIScrollView instead (as opposed to the original plan which was to embed the UIScrollView in a UIView)
I went ahead and changed the class on the UIView to UIScrollView. IB changed it to Scroll View in the Document Outline, I figure I'm good, right?
And then I see the behavior you describe.
At some point it hits me that this isn't sufficient. Apparently adding a UIScrollView via IB does some things differently than adding a UIView and just changing class isn't enough. And this is probably the reason re-doing it from scratch fixed it for you.
So for anyone who runs into this in the future, make sure you added the UIScrollView via IB instead of a UIView
I was having the same problem as described in this post. I tried multiple combinations of solutions that did not work, including:
putting the scroll view inside a view with Clip To Bounds = YES
putting a view inside the scroll view with Clip To Bounds = YES, that then contained my child view
putting a Container View inside the scroll view, and then embedding my subview
rebuilding the Interface Builder files completely
every combination of autosizing mask options systematcially for both the scroll view and container view
clip to bounds enabled or disabled for every single element systematically
The child view in question had previously worked inside a scroll view, but wouldn't in this one case where the content blew outside the bounds of the scroll view.
In the end, I implemented the solution in code as I could find no way to get Interface Builder to co-operate:
// There are two scroll areas on the screen, the left view and the right view.
// We want the right view to contain a scrollable area with another child view controller
// we designed in Interface Builder.
// create a scroll view to fill the right view with a scrollable area
CGSize rightFrameSize = self.rightView.bounds.size;
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake (0, 0, rightFrameSize.width, rightFrameSize.height)];
scrollView.contentSize = CGSizeMake(640, 1352);
[self.rightView addSubview:scrollView];
// now create our child view controller from Interface Builder and add it to the scroll view
UIStoryboard *sb = [UIStoryboard storyboardWithName:#"CustomerAddress" bundle:[NSBundle mainBundle]];
detailsView = [sb instantiateViewControllerWithIdentifier:#"CustomerDetailsView"];
detailsView.delegate = self;
detailsView.customer = _customer;
[scrollView addSubview:detailsView.view];
You could of course get the scrollView.contentSize from the child view controller you constructed in Interface Builder using scrollView.contentSize = detailsView.view.frame.size.
I have a love/hate relationship with Interface Builder... most days I love it, but some days we argue and I wish we'd never met... :)
only make cliptobound=YES in storyboard if you changed UIView to UIScrollView
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.