Apple's tab bar controller has a lot of limitations. One important limitation is that you can't modify the tab bar in a rejection safe mode. My tab bar has a simple sliding movements and it's multi row.
For those reasons I decided to build a TBVC from the beginning; everything seems to work correctly, but I'm really messing around with rotation. Every time that I change orientation main view frames are changed.
Here is my hierarchy from top to the container view:
-MainView--contains-->TabBarView+containerView
The containerView is the view used to contain views loaded from the other controllers.
Here is the -loadView method of my CustomTabBaViewController
- (void)loadView
{
UIView *theView=[[UIView alloc]initWithFrame:[[UIScreen mainScreen]bounds]];
theView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
theView.backgroundColor=[UIColor greenColor];
containerOfControllersView=[[UIView alloc] initWithFrame:theView.bounds];
containerOfControllersView.backgroundColor=[UIColor blueColor];
containerOfControllersView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
[theView addSubview:containerOfControllersView];
ideoloTabBar=[[IdeoloTabBar alloc]initWithNumberOfControllers:[controllers count]];
[theView addSubview:ideoloTabBar];
self.view=theView;
[theView release];
}
When I set a new view from another controller I use this method:
-(void)setCurrentViewWithView:(UIView*)theView{
if ([[self.containerOfControllersView subviews] count]>0) {
UIView *tagView=[self.containerOfControllersView viewWithTag:555];
tagView.tag=0;
[tagView removeFromSuperview];
}
theView.tag=555;
theView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
theView.frame=[[UIScreen mainScreen]applicationFrame];
[self.containerOfControllersView addSubview:theView];
[self.view bringSubviewToFront:ideoloTabBar];
}
As you can see the views from other view controllers are applied using the applicationFrame.
When I rotate the device happens something wrong, the mainview not only is resized according to the new orientation but also moved by 20px (status bar size) to the botton, thus leaving a gap between the status bar and the container view. Since I gave the mainview the screen bounds I can't understand with it should be moved.
UPDATE
I'm trying a different approach so I've modified the loadView like that:
- (void)loadView
{
[super loadView];
containerOfControllersView=[[UIView alloc] initWithFrame:self.view.bounds];
containerOfControllersView.backgroundColor=[UIColor blueColor];
containerOfControllersView.autoresizingMask=UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth;
self.view.backgroundColor=[UIColor greenColor];
[self.view addSubview:containerOfControllersView];
ideoloTabBar=[[IdeoloTabBar alloc]initWithNumberOfControllers:[controllers count]];
[self.view addSubview:ideoloTabBar];
}
And in the setCurrentViewWithView:(UIView*)theView I've modified the line with
theView.frame=self.view.bounds;
instead of using the applicationFrame.
NOW:
On iPhone when I try to load a modalView it cuts about 40px at the bottom
On iPad when I try to load a modalView it lefts 20px at the bottom, because 20px are under the status bar but wantsFullScreen is NO.
UPDATE 2
It seems that the presentModalViewController should be called from the root view controller. I will create a protocol and an abstract UIViewController subclass to implement it an load it correctly.
Any suggestion? work around?
I don't like the approach of creating an entirely custom TabBarController from scratch. I like to put a custom view on top of a real TabBar as a subview, and then pass all the button presses to the real TabBarController. This way you don't have to code a window manager yourself.
- (void)tabButtonPressed:(id)sender
{
UIButton *button = (UIButton *)sender;
if (button.tag == HomeButton)
self.tabBarController.selectedIndex = 0;
// etc.
}
This should also be rejection safe.
This has been addressed here: Application frame leaves blank line at the top
But, you could also specify your frame by subtracting 20 from y:
CGRect rect = [[UIScreen mainScreen] applicationFrame];
theView.frame = CGRectMake(rect.origin.x, rect.origin.y - 20, rect.size.width, rect.size.height);
It has been a while since I'm using my custom TabBarViewController with disappearing tabbar and it seems to work properly both on iPad and iPhone.
The main problem that I had was due to an incorrect assignment to the content view frame and probably to a wrong assumption that modalVC were loaded from the current view controller.
First point: the content view should use the bounds of the main view, here is a part of the loadView method of the Root View Controller:
[super loadView];
containerOfControllersView=[[UIView alloc] initWithFrame:self.view.bounds];
Second:before add as a subview a view of a view controller remark to it that its frame should have the same bounds of its new parent view.
theView.frame =self.view.bounds;
Third: modal view controllers should be loaded from the root view controller or the will never have correct size. That's why I've implemented a base abstract class for each view controllers that inherit a protocol that manage the presetation and dismissing of modal viewcontrollers.
Hope this helps someone else.
Andrea
Related
Hi In my app Intially i loaded a view from app delegate. Next when some button click on this root view I added this code.
-(IBAction)method
{
View1 *v1=[[View1 alloc] init];
CGRect frame = CGRectMake(0.0f, 100.0f, 320.0f, 250.0f);
v1.view.frame=frame;
hideView = [[UIView alloc] initWithFrame: CGRectMake(0,0,320,480)];
hideView.backgroundColor = [UIColor blackColor];
hideView.alpha =0.8;
[hideView addSubview:v1.view];
[self.view addSubview:hideView];
}
In above code View1 is another view controller and hide view is just a view in root view controller. Now totally for Root view controller two views are added. Now my question is i want to remove the two added subviews from Root view when i clicked on table cell. how to do this? And also when i added this sub views to root view scrolling also not possible to table view and saying bad access error how to resolve this? Or any other ways to do this.
You can set unique tag to any view (subview) to easily retrieve reference to them later.
In your case, for example, you can add following lines:
v1.tag = 10112;
hideView = 10113;
And get references to that views later you can type:
//view - is a root view of that subviews
View1 *v1 = [view viewWithTag:10112];
UIView *hideView = [view viewWithTag:10113];
// and now you are able to remove them:
[v1 removeFromSuperview];
[hideView removeFromSuperview];
If View1 is inside hideView, so you can just call [hideView removeFromSuperView]; to remove both, or just use the #Nekto solution to recognize the view you want to remove.
For the scrolling issue, it's normal that if you add a view on top of the UITableView you're going to loss the table's scrolling, you could look at hitTest method of UIView.
I am new to iPad developer,
I made one Registration form in my application, when i see my application in Portrait mode,
i am able to see whole form with no scrolling, but when i see same form in Landscape mode, i am not able to see part which is at bottom of page, for that a scrolling should be there to see bottom part.
:
In my .h file when i replace
#interface ReminderPage : UIViewController{
...
...
}
:UIViewController with :UIScrollView
and then when i add label in my .m file like this,
UILabel *Lastpaidlbl = [[[UILabel alloc] initWithFrame:CGRectMake(70 ,400, 130, 50)]autorelease];
Lastpaidlbl.backgroundColor = [UIColor greenColor];
Lastpaidlbl.font=[UIFont systemFontOfSize:20];
Lastpaidlbl.text = #"Lastpaid on :";
[self.view addSubview:Lastpaidlbl];
I am getting error on last line Property view not found on object of type classname.
i am unable to add label in my view.
Any help will be appreciated.
The question appears to be really asking how can all the components on the screen be placed inside a UIScrollView, rather than a UIView. Using Xcode 4.6.3, I found I could achieve this by simply:
In Interface Builder, select all the sub-views inside the main UIView.
Choose Xcode menu item "Editor | Embed In | Scroll View".
The end result was a new scroll view embedded in the existing main UIView, will all the former sub-views of the UIView now as sub-views of the UIScrollView, with the same positioning.
If you want to replace your UIViewController with a UIScrollView, you will have to go a bit of refactoring to your code. The error you get is just an example of that:
the syntax:
[self.view addSubview:Lastpaidlbl];
is correct if self is a UIViewController; since you changed it to be UIScrollView, you should now do:
[self addSubview:Lastpaidlbl];
You will have quite a few changes like this one to make to your code and will face some issues.
Another approach would be this:
instantiate a UIScrollView (not derive from it);
add your UIView (such as you have defined it) to the scroll view;
define the contentSize of the scroll view so to include the whole UIView you have.
The scroll view acts as a container for your existing view (you add your controls to the scroll view, then add the scroll view to self.view); this way, you could integrate it within your existing controller:
1. UIScrollView* scrollView = <alloc/init>
2. [self.view addSubview:scrollView]; (in your controller)
3. [scrollView addSubview:<label>]; (for all of your labels and fields).
4. scrollView.contentSize = xxx;
I think the latter approach will be much easier.
Please put all of your UIComponents to the UIScrollview and then it will start scrolling.
please look in to content size. please change it according to the orientation of device.
You're subclassing UIScrollView, so there is no self.view because already self is the view (of the scrollview). You dont need to subclass the scrollview, you can just embed your components in a ivar scrollview and set its contentSize (in your case, you have to enable the scrolling just when the device is in landscape mode). In interface builder you can embed the selected elements in one click, Editor-> Embed in-> scrollview.
First create scrollview
UIScrollView * scr=[[UIScrollView alloc] initWithFrame:CGRectMake(10, 70, 756, 1000)];
scr.backgroundColor=[UIColor clearColor];
[ self.view addSubview:scr];
second
change [self.view addSubview:Lastpaidlbl];
to
[scr addSubview:Lastpaidlbl];
third
set height depends on content
UIView *view = nil;
NSArray *subviews = [scr subviews];
CGFloat curXLoc = 0;
for (view in subviews)
{
CGRect frame = view.frame;
curXLoc += (frame.size.height);
}
// set the content size so it can be scrollable
[scr setContentSize:CGSizeMake(756, curXLoc)];
Finally
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Override to allow orientations other than the default portrait orientation.
if (interfaceOrientation==UIInterfaceOrientationLandscapeLeft || interfaceOrientation==UIInterfaceOrientationLandscapeRight) {
self.scr.frame = CGRectMake(0, 0, 703,768);
} else {
self.scr.frame = CGRectMake(0, 0, 768, 1024);
}
return YES;
}
I am presenting a modal view controller. If it matters, it is scrolling up from the bottom. How can I control what portion of the screen it occupies?
EDIT: I have the following in the modal view controller. It's not helping.
- (void)viewDidLoad {
TestResultView *trv = [[TestResultView alloc]initWithTest: [Model m].currentTest];
self.view = trv;
trv.frame = CGRectMake(0, 320, 320, 160);
[trv release];
[super viewDidLoad];
}
You can modify the frame of the view controller, but if you're using UIViewController's -presentModalViewController:animated: method, the view behind will be unloaded once your modal view is finished animating onto the screen (This assumes you're on an iPhone) and you'll see a white screen where your background view should be. iOS assumes that your modal view controller will be a full-screen view controller, and dumps the other view to save memory.
If you really want to show a view over part of the screen, you should instead add the UIView (no UIViewController) to your current UIViewController's view as a subview, and then animate it onscreen yourself. I think something like this would work in your UIViewController class that will present the view:
// Add the view as a subview and position it offscreen just below the current view
UIView *myHalfView = [[UIView alloc] initWithFrame:someAppropriateFrame];
[self.view addSubview:myHalfView];
CGRect offScreenFrame = myHalfView.bounds;
offScreenFrame.origin = CGPointMake(0.0, CGRectGetMaxY(self.view.frame));
// Now animate the view upwards
[UIView beginAnimations:nil context:nil];
// Move the view upwards the height of your sliding view so it's entirely onscreen
myHalfView.center = CGPointMake(myHalfView.center.x, myHalfView.center.y - myHalfView.bounds.size.height);
[UIView commitAnimations];
[myHalfView release];
For bonus points, you could fade the view in by setting
myHalfView.alpha = 0.0;
before the UIView animation block, and setting
myHalfView.alpha = 1.0;
inside the block after animating the center property.
When you're done, you can do something similar but in reverse to slide the view offscreen. You can add an animationDidStop selector to the UIView animation block to be notified when the view has slid off screen so that you can remove it from the view hierarchy.
From an aesthetic point of view, you should also be careful how you do this since having a view slide up is a standard behavior, and if your view looks like a normal view but stops halfway, users may feel (even briefly) that the app has frozen. They'll figure it out, but it will leave a bad feeling about your app if not handled carefully. Mainly, I would avoid using standard full-screen cues like including a UINavigationController at the top of your view to help users understand what's going on. Half-sheets tend to be UIActionSheets on the iPhone, so think in that direction.
That is nice, the above accepted answer explains a nice hack to present subViews which feel like ModalViews, but what if it is an iPad, and i can indeed give it a modalViewController which doesnt cover the entire screen.
In case of iPads, I dont think the underneath view will be unloaded. ( because there are options where we can present the modalView on iPads, which dont cover the entire screen )
ModalViewController in the end is a controller itself, and like any other controller has a root view, whose properties can be editted, if we can get hold of it.
Here is what will give you a custom frame of the ModalView :
MyViewController *viewController = [[MyViewController alloc] init];
viewConroller.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:viewController animated:YES];
//superView of viewController's view is modalViewController's view, which we were after
viewController.view.superview.frame = CGRectMake(x,y,w,h);
//x y w h - can have desired values.
I would add to #dsaw's answer that the superview of the modal view does not seem to rotate its coordinate system in landscape mode. Here is the code that I used in my own app:
MyViewController* modalVC = [[MyViewController alloc] init];
modalVC.modalPresentationStyle = UIModalPresentationFormSheet;
[self presentModalViewController:modalVC animated:NO];
CGRect r = CGRectMake(self.view.bounds.size.width/2 - 236,
self.view.bounds.size.height/2 - 130,
472, 260);
r = [self.view convertRect:r toView:modalVC.view.superview.superview];
modalVC.view.superview.frame = r;
While the superview may not rotate itself with the iPad, it does seem to do the right thing and keep the modal view centered if I rotate the iPad after showing the modal view.
I have an issue with a UINavigationBar and its y-offset. The bar is displayed without a UINavigationController as superview, yet that should not matter. In the viewController where the navigation bar appears the setup looks like this:
// Add Basic View
CGRect viewFrame = [[UIScreen mainScreen] applicationFrame];
UIView *myView = [[UIView alloc] initWithFrame:viewFrame];
myView.backgroundColor = [UIColor greenColor];
self.view = myView;
[myView release];
UINavigationBar *myBar = [[UINavigationBar alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 50)];
.... add some Stuff to the bar...
[self.view addSubview:myBar];
[myBar release];
As I add the navigationBar as a chield View to self.view I assumed that origin.y = 0 would mean that the bar should get directly displayed below the status bar. This works as expected if I start the app on my iPad, rotate it once (or more) and then drill down to the view that is described above. In this case the UINavigationBar is displayed properly. Yet if I start my app and directly drill down to the controller described above (without rotating the device before this particular controller appears) the navigation bar slides 20 points below the status bar. But as soon as I rotate the device then, the bar is fine again. I have checked the viewFrame.origin.y value and it is 20 points in both situations, hence I do not understand why in one case the bar just seems to ignore the origin.y value of its superview but does not in the other.
I am really confused about this, has anybody else ever experienced such an issue?
Thanks a lot for your help!
Ps. I have also tried it with a UIToolbar, the problem is the same.
Yes. My solution is to set the "Full screen on launch" flag to on in Interface Builder for the window in the MainWindow-iPad.xib file. Then design your views as if the 20 pixel status bar were always displayed, so in my root view, I have a toolbar that is positioned 20px below the top of the screen in the content view.
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;
}