i have a UIView that is smaller than the superview so i can represent this view as a modal view when a button is clicked.
I have managed to do the following:
* add a subview to the superview.
* centered this modal view
I am now trying to make the elements behind the UIView unclickable. And also add a grey shadow te the ourside of my modal view so that the user understands that the modal view is the view in focus.
I would like to know how to achieve this.
I do not wish to use the presentation modal transition. I know and have already implemented this in other projects.
Any help is appreciated.
The simplest thing would be to lay a fullscreen UIView with a translucent gray background behind your "modal" view. Then it will intercept all of the touches. It might look something like this:
UIView *dimBackgroundView = [[UIView alloc] initWithFrame:theSuperview.bounds];
dimBackgroundView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:.5f];
[theSuperview addSubview:dimBackgroundView];
[theSuperview addSubview:modalView];
For future reference, you can set myView.userInteractionEnabled = NO to disable touch events on a view.
There are several ways to do it.
If you have a custom view which has custom location, you can modify it like that:
Create an instance var:
UIView* backgroundView;
And whenever you need it, put it behind your custom view:
if (backgroundView == nil)
backgroundView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, [UIScreen mainScreen].bounds.size.height, [UIScreen mainScreen].bounds.size.width)];
backgroundView.backgroundColor = [[UIColor grayColor] colorWithAlphaComponent:.5f];
[self.view addSubview:backgroundView];
[backgroundView animateBump:customView.view];
[backgroundView addSubview:customView.view];
When you do not need it anymore
[backgroundView removeFromSuperview];
Related
I'm adding a series of buttons to a UINavigationBar using:
NSArray *items;
items = [NSArray arrayWithObjects:
fixedSpace,
refreshStopBarButtonItem,
flexibleSpace,
self.backBarButtonItem,
flexibleSpace,
self.forwardBarButtonItem,
flexibleSpace,
self.actionBarButtonItem,
fixedSpace,
nil];
UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectMake(0.0f, 0.0f, toolbarWidth, 44.0f)];
toolbar.items = items;
toolbar.tintColor = [[UIColor whiteColor] colorWithAlphaComponent:1.0];
toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight;
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithCustomView:toolbar];
All working well.
However when I rotate to landscape mode the toolbar within the uinavigationbar doesn't rotate.
Adding this code (found on SO) causes the toolbar to resize but not the buttons within it, so they are partially cropped at the bottom and no longer lines up with the toolbar background
- (void)willAnimateRotationToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
CGRect navigationToolbarFrame = self.navigationController.navigationBar.frame;
CGRect customToolbarFrame = CGRectOffset(navigationToolbarFrame, 0.0, navigationToolbarFrame.size.height);
[UIView animateWithDuration:duration animations:^{
//self.toolbar.frame = customToolbarFrame;
// FAILS!!!
//self.navigationItem.rightBarButtonItem.toolbar.frame = customToolbarFrame;
// FAILS!!!
}];
}
What is the correct way to address the toolbar within the uinavigationbar?
Something like...
self.toolbar.frame = customToolbarFrame;
Or do I have to specify a autoresizemask for the UIBarButtonItems?...
self.backBarButtonItem.autoresizingMask = UIViewAutoresizingFlexibleHeight;
... trying to do so like this fails
Very curious because this code rotates toolbar fine when I include it in my code. No problem rotating the toolbar.
I assume your view controller is responding to shouldAutorotateToInterfaceOrientation? Could you include screen snapshot of what you're seeing?
Are you doing any UIToolbar category/subclass to eliminate its border? (I just subclass with empty (void)drawRect:(CGRect)rect to get rid of border, but I tried both that and the standard UIToolbar and both rotated fine.) Anyway, if you're doing subclass/category of UIToolbar, please include that code?
Also, you could alternatively just use iOS 5's rightBarButtonItems and bypass the toolbar altogether, e.g. self.navigationItem.rightBarButtonItems = items; will then add the array of UIBarButtonItem objects to the navigation bar.
This is a bit of a long shot, but how is your view controller being loaded? Some people try bypassing presentViewControllerAnimated and/or pushViewController and instead simply create a view controller, grab its view, add it as a subview of the previous view controller's view. Unfortunately, this ends up with a disconnect between the view controller hierarchy and the view hierarchy, and according to WWDC 2011 session 102 on view controller containment, this can prevent rotation events from being transmitted correctly. Make sure you're using presentViewControllerAnimated or pushViewController if this isn't your root view controller.
I don't do any of that willAnimateRotationToInterfaceOrientation or subsequent code, just the simple UIToolbar and it works fine during rotation, so I wonder if the problem rests elsewhere.
I know if I want to add a black background to a UIImageView, I can create an UIImageView that is a bit smaller than the UIView it's contained in. If I do this, the UIImageView is positioned on top of the black background.
Just hypothetically speaking, suppose I want the black background UIView on TOP of the UIImageView (so that it covers the image), how do I do this? Is there a concept of a z-index?
Here's my code to add a black background to an image:
UIView *blackBG = [[UIView alloc] initWithFrame:CGRectMake(0,0,100,100)];
blackBG.backgroundColor = [UIColor blackColor];
UIImageView *myPicture = [[UIImageView alloc] initWithImage:
[UIImage imageNamed: #"myPicture.jpg"]];
int borderWidth = 10;
myPicture.frame = CGRectMake(borderWidth,
borderWidth,
blackBG.frame.size.width-borderWidth*2,
blackBG.frame.size.height-borderWidth*2)];
[blackBG addSubview: myPicture];
Subviews are layered, you can send a subview to the back with...
[myView sendSubviewToBack:mySubView];
or bringSubviewToFront etc.
You can also do one of these...
[myView insertSubview:mySubview atIndex:0]; //Places it at the bottom
[myView insertSubview:mySubview belowSubview:anotherSubview]; //Places it below anotherSubview
As soon as you add a view as a subview to another view however, the parent view is the bottom of the stack. You can play around with CALayer z orders but you are bypassing the general approach of views and subviews.
Create a single view as a parent for both the bg view and the imageview, then you can order them how you like.
UPDATE: I have a UIView+Layout category which has nice [subview sendToBack]; and [subview bringToFront]; which you may find useful.... article here
I have made a custom UIView which is shown when the user hits a button in the navigationbar. I make my view's in code. In my loadview I set the autoresizing masks and the view loads correct on screen. However the UIView which is shown when the user taps the button does not resize even when I have set the autoresizing masks.
UIView *blackView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320.0, 416.0)];
blackView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
Do I need to use self.view.frame.size.width and self.view.frame.size.height instead? And if I do why? Does not resizing masks work outside of loadView?
Thank you for your time:)
the autoresizingMask affects how a view will behave when its superviews frame changes. if all you are doing is showing theblackViewwhen you tap a button, thenblackView` will have whatever frame you initially set for it.
If this isn't enough info, please post some more code around how you are configuring and displaying blackView and it's superview and explain more about what situations you are expecting blackView to resize in. Rotation is one of them, if that's what you're concerned with.
First things first, I hope you've done this:
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
Let's say the view that needs resizing is: view2
The view that has view2 as a subview is: view1
While creating view1 you would declare it as:
view1 = [[UIView alloc] init];
[view1 setNeedsLayout];
Now in view1's .m file you need to overload the layoutSubviews method as shown:
- (void)layoutSubviews
{
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
In case view1 is a view controller's view, you need to do that same thing as above in the willRotate method as shown
- (void)willRotateToInterfaceOrientation:(UIInterfaceOrientation)toInterfaceOrientation duration:(NSTimeInterval)duration
{
[super willRotateToInterfaceOrientation:toInterfaceOrientation duration:duration];
CGRect frame = view2.frame;
// apply changes to frame
view2.frame = frame;
}
This is a tried and tested method that I use to handle orientation changes.
I'm trying to do something that shouldn't be that complicated, but I can't figure it out.
I have a UIViewController displaying a UITableView. I want to present a context menu when the user press on a row. I want this to be a semi-transparent view with labels and buttons.
I could use an AlertView, but I want full control on the format of the labels and buttons and will like to use Interface Builder.
So I created my small view 250x290, set the alpha to .75 and create a view controller with the outlets to handle the different user events.
Now I want to present it.
If I use presentModalViewController two (undesired) things happen
1) the view covers all of the screen (but the status bar).
2) It is semi-transparent, but what I see "behind" it its not the parent view but the applications root view.
Ive tried adding it as a subview, but nothing happens, so Im not doing something right:
RestaurantContextVC* modalViewController = [[[RestaurantContextVC alloc] initWithNibName:#"RestaurantContextView" bundle:nil] autorelease];
[self.view addSubview:modalViewController.view];
Is it possible to do what I want?
Thanks in advance.
Gonso
I'm coding similar thing. My approach include.....
Not using dismissModalViewControllerAnimated and presentModalViewController:animated.
Design a customized full sized view in IB. In its viewDidLoad message body, set the background color to clearColor, so that space on the view not covered by controllers are transparent.
I put a UIImageView under the controllers of the floating view. The UIImageView contains a photoshoped image, which has rounded corners and the background is set to transparent. This image view serves as the container.
I uses CoreAnimation to present/dismiss the floating view in the modal view style: (the FloatingViewController.m)
- (void)viewDidLoad
{
[super viewDidLoad];
[self.view setBackgroundColor:[UIColor clearColor]];
[UIView beginAnimations:nil context:nil];
[self.view setFrame:CGRectMake(0, 480, 320, 480)];
[UIView setAnimationDuration:0.75f];
[self.view setFrame:CGRectMake(0, 0, 320, 480)];
[UIView commitAnimations];
}
wangii
Thats pretty much the solution I found.
I load the view with loadNibNamed and then just add it on top with addSubView, like this:
//Show a view on top of current view with a wait indicator. This prevents all user interactions.
-(void) showWaitView{
NSArray* nibViews = [[NSBundle mainBundle] loadNibNamed:#"WaitView" owner:self options:nil];
#ifdef __IPHONE_2_1
waitView = [ nibViews objectAtIndex: 0];
#else
waitView = [ nibViews objectAtIndex: 1];
#endif
CGFloat x = self.view.center.x - (waitView.frame.size.width / 2);
CGFloat y = self.view.center.y - (waitView.frame.size.height / 2);
[waitView setFrame:CGRectMake(x,y,waitView.bounds.size.width,waitView.bounds.size.height)];
[self.view addSubview:waitView];
}
Could you elaborate on points 3 and 4?
What I did to give the view the round rect aspect is put it inside a round rect button.
This code will actually allow you to have a small floating view, but if the view is smaller that its parent, the user could interact with the visible part of the parent.
In the end I create my view with the same size, but kept the code just in case.
Gonso
I would strongly consider using a navigation controller to slide in your subview instead of overlaying it. This is the expected model and any small benefit you may think you'll get by doing it your own way will be greatly offset by the principle of (least) surprise.
If you really really have to do it this way, I believe the trick is to add the first table view as a subview of a transparent "holding" view that the view controller maintains. Then add your new sub view as another subview of that.
Again, if you really want to do this, instead of adding a transparent "holding" view, since this pop-up is essentially modal, I would make it a subview directly of the window.
You might want to put in a transparent black shield behind it to prevent touches on the background and focus input on the popup.
But seriously, consider either popping a controller on the stack or using that alert view. Unless you've hired a $$ designer, it's probably not going to look appropriate on the iPhone.
What I did was create a UIViewController on top of my UINavigation controller in my app delegate and made it a property of a singleton object for convenience:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//--- create root navigation controller
self.window.rootViewController = self.navigationController;
//--- create view controller for popups:
popupViewController = [[BaseViewController alloc] init];
popupViewController.view.backgroundColor = [UIColor clearColor];
popupViewController.view.hidden = true; //for rendering optimisation
[self.window addSubview:popupViewController.view];
[AppState sharedInstance].popupViewController = self.popupViewController;
//--- make all visible:
[self.window makeKeyAndVisible];
return YES;
}
At any point in my app, I can then call e.g.
MyViewController * myVC = [[UIViewController alloc] init];
//... set up viewcontroller and its view...
// add the view of the created view controller to the popup view:
[AppState sharedInstance].popupViewController.view.hidden = false;
[[AppState sharedInstance].popupViewController.view addSubview:myVC.view];
The BaseViewController used on the top just inherits from UIViewController and sets up a full-screen view:
//----- in BaseViewController implementation
- (void)loadView {
//------- create root view:
CGRect frame = [[AppState sharedInstance] getScreenFrame];
rootView = [[VCView alloc] initWithFrame:frame];
rootView.backgroundColor = [UIColor whiteColor];
self.view = rootView;
}
I am pretty new to iphone programming. I just started about a month ago and have only been tinkering with small tutorial type applications but anyways, here is my question.
I currently have a UIScrollView thats scrollable and zoomable, that loads a UIImageView subview, and i want to add in some controls(UIButtons) over the image view but when i zoom in and out the whole group(the buttons and the image) zoom togeather.
If I add the UIButtons to my UIScrollView and zoom, the image zooms and the buttons stay in origional place
If I add the UIButtons to my UIImageView they zoom correctly but arent buttons anymore. IE they lose their interactivity.
Later ill add lots of buttons into an array and add them.
I would appreciate any help i can get
This is part of my mainViewController.m:
- (void)viewDidLoad
{
[scrollView2 setBackgroundColor:[UIColor blackColor]];
[scrollView2 setCanCancelContentTouches:NO];
scrollView2.clipsToBounds = YES; // default is NO, we want to restrict drawing within our scrollview
scrollView2.indicatorStyle = UIScrollViewIndicatorStyleWhite;
scroll2ImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"bigNH.jpg"]];
[scrollView2 addSubview:scroll2ImageView];
[scrollView2 setContentSize:CGSizeMake(scroll2ImageView.frame.size.width, scroll2ImageView.frame.size.height)];
scrollView2.minimumZoomScale = .5;
scrollView2.maximumZoomScale = 3;
scrollView2.delegate = self;
[scrollView2 setScrollEnabled:YES];
UIButton *myButton = [UIButton buttonWithType:UIButtonTypeCustom];
CGRect newSize = CGRectMake(658, 435, 50, 50); // position in the parent view and set the size of the button
myButton.frame = newSize;
[myButton setImage:[UIImage imageNamed:#"redStop.png"] forState:UIControlStateNormal];
// add targets and actions
[myButton addTarget:self action:#selector(buttonClicked:) forControlEvents:UIControlEventTouchUpInside];
// add to a view
[scroll2ImageView addSubview:myButton];
[redLineArray addObject:myButton];
[scroll2ImageView release];
}
This might not help and it is not what it appears that you want to do.
However, when I want buttons associated with a scrollview, I add a parent view, add the buttons to that view and also add the scrollview to that view. The button(s) and the scrollview would be siblings. This allows the user to scroll around the contents of the scrollview, while insuring that the buttons are always in view.
-isdi-
By default, UIImageView has userInteraction disabled. Have you changed this?
Also, I would be really uneasy adding subviews to an UIImageView, it's really meant to hold one image. It would be better to create a normal UIView and add the UIButtons and UIImageView to that. It's very easy to control the ordering so that the buttons appears on top of the image.
P.S. Just to make your life easier, instead of :
[scrollView2 setContentSize:CGSizeMake(scroll2ImageView.frame.size.width, scroll2ImageView.frame.size.height)];
You can use :
[scrollView2 setContentSize:scroll2ImageView.frame.size];
Make a single view to contain all your zoomable content (everything it would appear), and call it say contentView. Now place everything you want in that contentView, the imageView and all your buttons etc.
In your UIScrollView delegate place this: (Be sure to set the scrollView's delegate if you haven't already)
- (UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return self.contentView;
}
Only one view is allowed to zoom, but that one view can however contain other views that will scale because they're child views.
Also be sure to set the scrollView's contentSize value to the exact size of your zoomable contentView.