UPDATE
It turns out that the code below is not actually the problem. In my app delegate I am doing:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc] init];
self.window.rootViewController = self.viewController;// <-- this does not work
//[self.window addSubview:self.viewController.view]; // <-- this works
[self.window makeKeyAndVisible];
return YES;
}
If I remove the statement "self.window.rootViewController = self.viewController" and just add the viewController's view to the window, it works. Can anyone explain this? Does setting the rootViewController on the window constrain the child's bounds? I have tried to go through the docs, but it doesn't mention much about this.
ORIGINAL POST
I am having trouble adding padding to pages in a UIScrollView. I am basically trying to setup a simple scroll view that shows UIViews in different pages separated by a predefined padding (kind of like the Photos app without photos). I have been trying to follow Apple's ScrollView example from WWDC 2010 and their sample app PhotoScroller but always come up with padding showing in the view. The app currently hides the status bar and adds 1 view controller to the window. To make things simple, each of the pages should show a UIView that is colored green, while the space where there is padding is yellow. You should only see the yellow when the user is scrolling. Here are the first 3 pages:
I have a single class level field called pagingScrollView declared in the .h file. In my single view controller, I am basically just trying to follow what the sample code is doing.
#define PADDING 10
#define PAGE_COUNT 3
- (void)loadView
{
CGRect pagingScrollFrame = [[UIScreen mainScreen] bounds];
pagingScrollFrame.origin.x -= PADDING;
pagingScrollFrame.size.width += (2 * PADDING);
pagingScrollView = [[UIScrollView alloc] initWithFrame:pagingScrollFrame];
pagingScrollView.pagingEnabled = YES;
pagingScrollView.backgroundColor = [UIColor yellowColor];
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT, pagingScrollFrame.size.height);
self.view = pagingScrollView;
for(int i = 0; i < PAGE_COUNT; i++) {
CGRect frame = [self frameForPageAtIndex:i];
UIView *page = [[UIView alloc] initWithFrame:frame];
page.backgroundColor = [UIColor greenColor];
[pagingScrollView addSubview:page];
}
}
- (CGRect)frameForPageAtIndex:(NSUInteger)index {
CGRect bounds = pagingScrollView.bounds;
CGRect pageFrame = bounds;
pageFrame.size.width -= (2 * PADDING);
pageFrame.origin.x = (bounds.size.width * index) + PADDING;
return pageFrame;
}
The pagingScrollFrame has a width of 340, so (I thought) that scroll view would be broken up into pages of 340 pixels. What am I missing?
Looking at this very briefly, it appears that you are doing things fairly correct, except for the setting of your content size. You set:
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT, pagingScrollFrame.size.height);
This would be correct if each of your pages was truly right next to each other, but as you are adding a 10pt pad between each view, you should have something like:
pagingScrollView.contentSize = CGSizeMake(pagingScrollFrame.size.width * PAGE_COUNT + PADDING * (PAGE_COUNT - 1), pagingScrollFrame.size.height);
This should correct your problem and cause the yellow to not be in the visible area.
The reason the paging is off is because setting the RootViewController on the window is apparently doing something behind the scenes (what that is, I don't know). To fix is, I use the old way of adding a view to the window.
[self.window addSubview:self.viewController.view];
If you think you know how to fix it while setting the RootViewController, please let me know!
Related
For the tutorial for my app, I am trying to create a UILabel that drifts across the displayed screen when a view appears and then is destroyed so that if the user comes back to that view during the tutorial, the UILabel will be created anew and again drift across the page. Here's a sample of one of the custom view controllers I am displaying with my UIPageViewController:
//this is just a custom UILabel with some padding
#property (nonatomic) PaddedLabel *directionsLabel;
//I have tried setting UILabel to nil or removing it from view
-(void)viewWillDisappear:(BOOL)animated
{
NSLog(#"view is disappearing");
//this does not remove the label
self.directionsLabel = nil;
//nor does this
[self.directionsLabel removeFromSuperview];
}
- (void)viewDidAppear:(BOOL)animated
{
[self messageTutorial];
}
- (void)messageTutorial
{
CGFloat width = [[UIScreen mainScreen] bounds].size.width;
CGFloat height = [[UIScreen mainScreen] bounds].size.height;
PaddedLabel *directionsLabel = [[PaddedLabel alloc] initWithFrame:CGRectMake(_width/2, _height/2, 100, 100)];
directionsLabel.text = #"test";
CGRect f = directionsLabel.frame;
f.size = size;
directionsLabel.frame = f;
directionsLabel.center = self.view.center;
f = directionsLabel.frame;
f = directionsLabel.frame;
f.origin.y = .1*height;
directionsLabel.frame = f;
[self.view addSubview:directionsLabel];
[UIView animateWithDuration:TUTORIAL_DISAPPEAR_TIME animations:^{
directionsLabel.alpha = .5;
CGRect f = directionsLabel.frame;
f.origin.y = height - f.size.height*1.4;
directionsLabel.frame = f;
NSLog(#"animating");
} completion:^(BOOL finished) {
[directionsLabel removeFromSuperview];
//this also doesn't actually remove the label
}];
}
The problem is that if the user pages back to see this view she now sees a new label and the old one, so that if you page back and forth back and forth you end up with many many labels all saying the same thing, in different stages of progressing across the screen.
How can I remove the UILabel when the view disappears and add/create a new one when the view appears/reappears?
Thank you.
The code in your viewWillDisappear method is backwards. You need:
- (void)viewWillDisappear:(BOOL)animated {
NSLog(#"view is disappearing");
[self.directionsLabel removeFromSuperview];
self.directionsLabel = nil;
}
As you had it, setting self.directionsLabel to nil before trying to remove it results in a no-op.
Also, be sure to set self.directionsLabel when you create the label.
Instead of setting your label to nil and effectively destroying the label object (assuming automatic reference counting is on) rather use the following method to hide and show the label as and when your need it.
[self.directionsLabel setHidden:YES]; // hides it
[self.directionsLabel setHidden:NO]; // shows it
You've got the right idea setting objects you're not using to nil and removing them from the super view but it's over kill. A UILabel object uses a negligible amount of memory and you're better off creating the object once and then changing it's properties as you need to.
You don't seem to be setting self.directionsLabel to anything when you create the directionsLabel inside the messageTutorial method. It is a local instance of the label there. You should set it in the method somewhere.
Afterwards, removing it from the superview in viewWillDisappear will work (tested to verify).
Can I have some UIView which will always appear on top in iOS?
There are lots of addSubview in my project but I need to have one small view which will always appear. SO is there any other option than
[self.view bringSubViewToFront:myView];
Thanks
One more option (especially if you want to overlap several screens, with logo for example) - separate UIWindow. Use windowLevel to set the level of new window.
UILabel *devLabel = [UILabel new];
devLabel.text = #" DEV ";
devLabel.font = [UIFont systemFontOfSize:10];
devLabel.textColor = [UIColor grayColor];
[devLabel sizeToFit];
CGSize screenSize = [[UIScreen mainScreen] bounds].size;
static UIWindow *notificationWindow;
notificationWindow = [[UIWindow alloc] initWithFrame:
CGRectMake(screenSize.width - devLabel.width, screenSize.height - devLabel.height,
devLabel.width, devLabel.height)];
notificationWindow.backgroundColor = [UIColor clearColor];
notificationWindow.userInteractionEnabled = NO;
notificationWindow.windowLevel = UIWindowLevelStatusBar;
notificationWindow.rootViewController = [UIViewController new];
[notificationWindow.rootViewController.view addSubview:devLabel];
notificationWindow.hidden = NO;
Another option is set layer.zPosition of your UIView.
You need to add
#import <QuartzCore/QuartzCore.h>
Framework to your .m file.
And set such like
myCustomView.layer.zPosition = 101;// set maximum value as per your requirement.
For more information about layer.zPosition read this documentation.
Discussion
The default value of this property is 0. Changing the value of this property changes the the front-to-back ordering of layers onscreen. This can affect the visibility of layers whose frame rectangles overlap.
The other option is to add other subviews below this always-on-top subview. For example:
[self.view insertSubview:subview belowSubview:_topSubview];
There's no solution with Interface Builder if you search for this kind. It should be done programmatically. If you don't want to use bringSubviewToFront: everytime, just insert other subviews below this one.
Many times your view did not appear in viewDidLoad or, if your view comes from parentViewController (for example in many transitions like modal segue..) your can see parentViewController only in viewDidAppear so:
Try to put bringSubviewToFront in :
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self.view bringSubViewToFront:myView];
// or if your view is attached in parentViewController
[self.parentViewController.view bringSubViewToFront:myView];
}
Good luck!
Can someone spend a second and give me a pointer please?
I've got a UIView attached to a UIWindow, and I'm playing with hitTesting of points (for reasons that are much more complex than I want to get into here).
Given a view attached as a sub view of a window, I would expect that hitTest would find the view, but it doesn't appear to:
- (void)test_hitTest_shouldFindTheViewAttachedToAWindow {
UIWindow *window = [[UIWindow alloc] initWithFrame:CGRectMake(0, 0, 210, 520)];
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 200, 500)];
[window addSubview:view];
// This is ok
GHAssertEquals([view hitTest:CGPointMake(110, 270) withEvent:nil], view, nil);
// This fails: why? I would expect it to return the view, but it return null.
GHAssertEquals([view.window hitTest:CGPointMake(110, 270) withEvent:nil], view, nil);
}
The point is clearing inside the bounds of the window, right? Why doesn't it find the view?
UIWindow is created invisible by default, which means hitTest:withEvent will ignore it. If you set view.window.hidden = NO then hitTest:withEvent will work as you expect it to.
I'm using Kal and am having trouble getting it to display properly.
Right now, in the Storyboard, I have a subview of the main view with the tag 1:
I have that subview fitted to the space between the navbar and tabbar:
The problem is that the calendar/tableview (Kal) is not appearing properly in that subview:
First there is that small grey bar above the month (maybe Kal's spacing for the iPhone's info bar?). Then the tableview at the bottom of the calendar is behaving as if it extends far below the tab bar. That is, the cells won't scroll properly, as can be seen by the cell labeled 11:58 which is peeking up from the bottom. (I have scrolled it as far as it will go.)
Here is where I'm setting the delegates and loading the view for the Kal calendar/tableview:
- (void) viewDidLoad
{
NSLog(#"DateTimeViewController - viewDidLoad");
[super viewDidLoad];
self.calendar = [[KalViewController alloc] init];
self.calendar.delegate = self;
self.calendar.dataSource = self;
[[self.view viewWithTag:1] addSubview:self.calendar.view];
NSLog(#"selected date = %#",self.calendar.selectedDate);
}
I had exactly the same problem, I went to KalViewController.m in loadView
and changed the code in this way:
// KalView *kalView = [[KalView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame] delegate:self logic:logic];
int width = [[UIScreen mainScreen] applicationFrame].size.width;
int height = [[UIScreen mainScreen] applicationFrame].size.height-93;
KalView *kalView = [[KalView alloc] initWithFrame:CGRectMake(0, 0, width, height) delegate:self logic:logic];
This solved me both the problem of the "header" margin and the table view size.
Just play with the height value.
Play around a little bit with your 'y' value in CGRectMake to get a required frame. try this
KalViewController *kalView = [[KalViewController alloc] initWithFrame:CGRectMake(0,-20,100,320)];
self.calender = kalView;
CGRectMake(<x position> , <y position> , <required height> , <required width>)
How would one go about creating an ipad app that has a similar view layout to the facebook app? That is, one big view in the center, and the smaller, menu-like controller on the left side gets visible when you slide the main view to the right?
Are they using a modified splitview layout, or is this a custom multi-layer layout?
I know that I probably must make use of some gesture recognizers, but can anyone point me into the right direction of how to remake the facebook app layout? E.g what would be the two main controllers (tableview on the left, custom view in fullscreen size in the middle, place above the tableview?), and how do i slide in/out the menu?
Thanks in advance
as a note: I only need landscape orientation, should make thins easier.
[edit] this is my current implementation with the viewdeckcontroller:
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
RootViewController* menu = [[RootViewController alloc] init];
UINavigationController* navController= [[UINavigationController alloc] initWithRootViewController:menu];
DetailViewController* center = [[DetailViewController alloc] init];
IIViewDeckController* rootController = [[IIViewDeckController alloc] initWithCenterViewController:center leftViewController:navController];
_menuController = rootController;
rootController.leftLedge = [[UIScreen mainScreen] bounds].size.width - 50.0;
self.window.rootViewController = rootController;
[self.window makeKeyAndVisible];
This is the rootviewcontroller class (left side menu-thing controller):
-(void)loadView
{
// [super loadView];
// self.tableView = [[[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped] autorelease];
self.view= [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 320, 704)];
UITableView* tableView= [[UITableView alloc] initWithFrame:CGRectZero style:UITableViewStyleGrouped];
[self.view addSubview:tableView];
self.tableView= tableView;
}
This is a drop-in Xcode project for exactly what you want. :)
https://github.com/devindoty/DDMenuController
IIViewDeckController implements sliding views (like in the Path and Facebook apps) and also supports rotation.
I know this could be a bit late.
Hard to be certain this is what you mean but for the width of your left slide out view, you can control how much of it is shown by modifying the leftLedge property to how many pixels from the edge should be covered by the centerViewController. ie. leftLedge=50 would make your left menu 320-50=270 visible, leftLedge=250 would make your left menu 320-250=70 visible. This is visible width not actual width.
In your appDelegate
IIViewDeckController *deckController = [[IIViewDeckController alloc] initWithCenterViewController:self.mainNavigationController leftViewController:self.sideMenuViewController];
deckController.panningMode = IIViewDeckNavigationBarPanning;
deckController.leftLedge = 50;
deckController.centerhiddenInteractivity = IIViewDeckCenterHiddenNotUserInteractiveWithTapToClose;