Why is there space between the bottom of the imageView and the first TableViewCell in the UITable? - objective-c

I have a view controller that sets up a UIImageView and a UITableView as follows in viewDidLoad:
// Root UIView
UIView *rootView = [[UIView alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]];
rootView.backgroundColor = [UIColor redColor];
self.view = rootView;
// Image View
NSString *path = [[NSBundle mainBundle] pathForResource:#"test320x180" ofType:#"JPG"];
UIImage *image = [[UIImage alloc] initWithContentsOfFile:path];
self.imageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, image.size.width, image.size.height)];
self.imageView.image = image;
[self.view addSubview:self.imageView];
// Table View
self.tableView = [[UITableView alloc] initWithFrame:rootView.frame style:UITableViewStylePlain];
self.tableView.autoresizingMask = UIViewAutoresizingFlexibleHeight|UIViewAutoresizingFlexibleWidth;
self.tableView.delegate = self;
self.tableView.dataSource = self;
self.tableView.contentInset = UIEdgeInsetsMake(image.size.height, 0, 0, 0);
self.tableView.contentOffset = CGPointMake(0, -image.size.height);
self.tableView.backgroundColor = [UIColor clearColor]; // so we can see the image view
[self.tableView reloadData];
[self.view addSubview:self.tableView];
There are 20 pts between the bottom of the image view, which has CGRect: (0, 0, 320, 180), and the first cell of the Table View whose origin is (0, 200), shown in red in the screen shot below.
In reveal, I see that the TableView starts 20 pts below the ImageView, screenshot below. My best guess is that the table view automatically accounts for the status bar but the image view does not.
My intention is to have the image and the first tableview cell flush, but I'm not sure how to guarantee this without adding the magic number 20 to my code.

[[UIScreen mainScreen] applicationFrame] returns a value that includes the entire height of the screen, including the height of the status bar, and always in screen coordinates.
For example on a 3.5-inch iPhone, in both portrait and landscape, you'll get the same application frame:
{ 0,0, 320, 480 } // iPhone 3.5-inch, applicationFrame in both landscape and portrait
When you assign a frame with 0 for origin.y to the image view, then add it via:
[self.view addSubview:self.imageView];
... then the self.imageView's top 20 pixels are hidden beneath the status bar.
Please note, sometimes the status bar's height is doubled, such as by a phone call, or recording audio within an app, or Personal Hotspot/tethering. To survive that, you need the value from:
CGSize size = [[UIApplication sharedApplication] statusBarFrame].size;
This is returned in Screen coordinates, so the returned rect may be:
{ 0,0, 320, 20 } // iPhone 3.5-inch, portrait
{ 0,0, 20, 480 } // iPhone 3.5-inch, landscape
{ 0,0, 320, 40 } // iPhone 3.5-inch, portrait when tethering or other phone call
{ 0,0, 40, 480 } // iPhone
So the quick solution is to use something like the above CGRect size code with the following:
CGFloat height = (size.width < size.height? size.width : size.height);
3.5-inch, landscape when tethering or other phone call
Set the size of the frame of rootView using a CGRect that is
adjusted for the status bar height, so the imageView.frame.origin.y == 0 is
not hidden by the status bar
Set imageView's frame origin y to the status bar height (which means the rootView's content is still overlapped by the status bar).

UITableView is a subclass of UIScrollView so check the contentInset property and also set the UIViewController property automaticallyAdjustsScrollViewInsets is set to NO.
I think automaticallyAdjustsScrollViewInsets applies insets to all scroll views to account for status bars, button bars and navigation bars.

I faced this same issue, while i was working in storyboard where my tableview was having same 20 pixel gap. so what you need to do is:
1.) select your view controller and in attribute inspector deselect "Adjust scroll view inset". (this will remove the gap - that tableview is presuming for navigation bar)
2.) select your tableView and in size inspector set the "section header height" == '1' (this will remove the 20 pixel gap - that tableview is presuming for status bar)
Hope will help you!
Thanks

Related

UISearchBar - addSubview issue?

I'm trying to add UISearchBar (fixed position!) on top of the UITableView.
CGRect rect = self.headerView.frame;
CGRect newRect = CGRectMake(0,
rect.origin.y + rect.size.height,
rect.size.width,
CZP_SEARCHBAR_HEIGHT);
UIView *view = [[UIView alloc] initWithFrame:newRect];
view.backgroundColor = [UIColor whiteColor];
Result (i got a white rect on position where i want my bar):
But if i want to add subview to my view, searchbar appear on 1st cell of tableview (below my view!)
[view addSubview:searchBar];
Here's one way to do it. It looks like you're trying to do it in code instead of a storyboard, so this is a code example. It also looks like you're doing it in a popover of sorts, I put together a quick project as an example that uses a popover, it doesn't look exactly like yours, but it's close enough to get you where you're trying to go I think.
First, here's the code sample, this is from the view controller that contains the header, search bar and tableview.
- (void)viewDidLoad
{
[super viewDidLoad];
// get the desired size for this popover and setup our header height
CGSize viewSize = self.preferredContentSize; // could also be self.view.bounds.size depending on where you're using it
CGFloat headerHeight = 44.0;
// setup our desired frames
CGRect headerFrame = CGRectMake(0, 0, viewSize.width, headerHeight);
CGRect searchContainerFrame = CGRectMake(0, headerHeight, viewSize.width, headerHeight);
// for this frame I'm simply centering it, there's better ways to do it but this is an example
CGRect searchBarFrame = CGRectMake(5, 5, searchContainerFrame.size.width - 10, searchContainerFrame.size.height - 10);
// set our tableview frame to be positioned below our header and search container frame
CGRect tableviewFrame = CGRectMake(0, headerHeight *2, viewSize.width, viewSize.height - (headerHeight * 2));
// create our header view and set it's background color
UIView *headerView = [[UIView alloc] initWithFrame:headerFrame];
headerView.backgroundColor = [UIColor orangeColor];
// create our container view to hold the search bar (not needed really, but if you want it contained in a view here's how)
UIView *searchContainer = [[UIView alloc] initWithFrame:searchContainerFrame];
searchContainer.backgroundColor = [UIColor greenColor];
// instantiate our search bar
UISearchBar *searchBar = [[UISearchBar alloc] initWithFrame:searchBarFrame];
// add the search bar to the container view
[searchContainer addSubview:searchBar];
// create our tableview and position it below our header and search containers
UITableView *tableview = [[UITableView alloc] initWithFrame:tableviewFrame];
tableview.backgroundColor = [UIColor blueColor];
[self.view addSubview:headerView];
[self.view addSubview:searchContainer];
[self.view addSubview:tableview];
}
That snippet gives me a popover with an orange header, a green/grey search bar and a tableview beneath it.
EDIT: If you're interested in looking through the project file that I used to put this together you can download it off github here

How to get full screen bounds with correct orientation?

guys.
I have such problem: I need to use full screen for my view. So I use
[_activityIndicator setFrame:[[UIScreen mainScreen] applicationFrame]];
It's work perfect, but only in portrait view. What to do if my app have to work in landscape too?
Your app has a root view controller, which rotates in response to device rotation. So set your frame in terms of the root view controller's view. Use its bounds, not its frame.
You can get a reference to the root view controller through the main window. You can get a reference to the main window through the shared application:
UIWindow* theWindow = [[UIApplication sharedApplication] keyWindow];
UIViewController* rvc = theWindow.rootViewController;
UIView* mainView = rvc.view;
UIActivityIndicatorView* act =
[[UIActivityIndicatorView alloc] initWithFrame: mainView.bounds];
[mainView addSubview: act];
And then you retain a reference to the activity indicator in a property and set it spinning.
To get current screen bounds i use:
CGRect screenBoundsDependOnOrientation()
{
CGRect screenBounds = [UIScreen mainScreen].bounds ;
CGFloat width = CGRectGetWidth(screenBounds) ;
CGFloat height = CGRectGetHeight(screenBounds) ;
UIInterfaceOrientation interfaceOrientation = [UIApplication sharedApplication].statusBarOrientation;
if(UIInterfaceOrientationIsPortrait(interfaceOrientation)){
screenBounds.size = CGSizeMake(width, height);
}else if(UIInterfaceOrientationIsLandscape(interfaceOrientation)){
screenBounds.size = CGSizeMake(height, width);
}
return screenBounds ;
}

setting view boundaries

I have a scrollview with an image as a subview. I would like to set the boundaries of the scrollview to be the size of the image view, so that you wouldn't be able to see any of the background.
I don't want this happening anymore.
The weird part is, that after you zoom in or out on the image, then the boundaries seem to fix themselves, and you can no longer move the image out of the way and see the background.
This is what I have going for code:
-(UIView *) viewForZoomingInScrollView:(UIScrollView *)scrollView
{
// return which subview we want to zoom
return self.imageView;
}
-(void)viewDidLoad{
[super viewDidLoad];
[self sendLogMessage:#"Second View Controller Loaded"];
//sets the initial view to scale to fit the screen
self.imageView.frame = CGRectMake(0, 0, CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
//setes the scrollview's delegate to itself
self.scrollView.delegate = self;
//sets the maximum zoom to 2.0, meaning that the picture can only become a maximum of twice as big
[self.scrollView setMaximumZoomScale : 2.5];
//sets the minimum zoom to 1.0 so that the scrollview can never be smaller than the image (no matter how far in/out we're zoomed)
[self.scrollView setMinimumZoomScale : 1.0];
[imageView addSubview:button];
}
I thought that this line would solve my problem
//sets the content size to be the size our our whole frame
self.scrollView.contentSize = self.imageView.image.size;
But like I said, it only works after I zoom in or out.
EDIT: When I switch
self.scrollView.contentSize = self.imageView.image.size;
to
self.scrollView.frame = self.imageView.frame;
It works like I want it to (you can't see the background), except the toolbar on the top is covered by the image.
imageView.image.size isn't necessarily the frame of the imageView itself, try setting the
scrollview.frame = imageView.frame
and then
scrollView.contentSize = imageView.image.size
Then you won't see any border. If you want the image to be the maximum size to start with,
do
imageView.frame = image.size;
[imageView setImage:image];
scrollView.frame = self.view.frame; //or desired size
[scrollView addSubView:imageView];
[scrollView setContentSize:image.size]; //or imageView.frame.size
To fix this, I ended up declaring a new CGRect , setting its origin to my scrollView's origin, setting its size with the bounds of my view, and then assigning this CGRect back to my scrollview frame
CGRect scrollFrame;
scrollFrame.origin = self.scrollView.frame.origin;
scrollFrame.size = CGSizeMake(CGRectGetWidth(self.view.bounds), CGRectGetHeight(self.view.bounds));
self.scrollView.frame = scrollFrame;

UIScrollView, Pull down effect similar to CalcBot

I'm trying to make a UI similar to CalcBot where you can pull down the view controller with your finger and it reveals a hidden background view.
I think this is done with the UIScrollView but cannot work out the exact method to have for example 2x Horizontal views with one of them having a vertical.
I do have the horizontal scroll working fine, i'm just stuck with how I can make screen 1 pull down with a vertical scroller.
Any tips or pointers would be appreciated.
Thanks Aaron
You can have nested scrollviews to get scrolling working in both directions. Let's have a vertical scroller inside a horizontal scroller (though you can very well do it the other way round too), so views 3 and 1 get into a vertical scrollview, which in turn gets inside a horizontal scroller along with view 2.
You could create such a view hierarchy in code (probably in the main viewcontroller's viewDidLoad). The following snippet assumes view1, view2 and view3 as webviews.
CGRect bounds = self.view.bounds;
// main guy is a horizontal scroller
UIScrollView *hScrollView = [[UIScrollView alloc] initWithFrame:bounds];
hScrollView.contentSize = CGSizeMake(bounds.size.width * 2, bounds.size.height);
hScrollView.delegate = self;
[self.view addSubview:hScrollView];
// the horizontal scroller contains a vertical scroller
UIScrollView *vScrollView = [[UIScrollView alloc] initWithFrame:bounds];
vScrollView.contentSize = CGSizeMake(bounds.size.width, bounds.size.height * 2);
vScrollView.delegate = self;
[hScrollView addSubview:vScrollView];
// add view3 and view1 to the vertical scroller
UIWebView *view3 = [[UIWebView alloc] initWithFrame:bounds];
[view3 loadHTMLString:#"<h1>3</h1>" baseURL:nil];
[vScrollView addSubview:view3];
UIWebView *view1 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, 0, bounds.size.height)];
[view1 loadHTMLString:#"<h1>1</h1>" baseURL:nil];
[vScrollView addSubview:view1];
vScrollView.contentOffset = CGPointMake(0, bounds.size.height);
// add view2 to the horizontal scroller
UIWebView *view2 = [[UIWebView alloc] initWithFrame:CGRectOffset(bounds, bounds.size.width, 0)];
[view2 loadHTMLString:#"<h1>2</h1>" baseURL:nil];
[hScrollView addSubview:view2];
// enable paging in both directions
hScrollView.pagingEnabled = TRUE;
vScrollView.pagingEnabled = TRUE;
That would have you scrolling in both directions but that would also let you scroll-right from view3 to view2. If you want to prevent that, you should disable scrolling on hScrollView whenever the vScollView is showing view3. You can do that in scrollViewDidEndDecelerating: in the delegate of vScrollView. Something like:
-(void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView {
if (scrollView == self.verticalScrollView) {
self.horizontalScrollView.scrollEnabled = (self.verticalScrollView.contentOffset.y > self.view.bounds.origin.y);
}
}

NavigationBar from UINavigationController not positioned correctly

So, my iPad program has a pseudo-split view controller (one that I implemented, not base SDK one), and was working correctly a while ago. It has the basic layout (UINavController for master, content view controller for detail on right), but I have it so the master view doesn't disappear when rotated into portrait view.
Recently, I added in a UITabBarController to contain the entire split view, which has made the navigation bar go wonky, while all the other views are positioned fine. In addition, the navigation bar only gets mispositioned when the program starts up while the iPad is in landscape, or upside-down portrait. If it starts out in portrait, everything is fine.
Example images can be found here:
http://profile.imageshack.us/user/Pzychotix
Image where the navigation bar is upwards is when I initially launch the program.
Image where the navigation bar is downwards is after I rotate once or more times.
Relevant Code:
RootViewController.m:
- (void)loadView {
navController = [[NavigationBreadcrumbsController_Pad alloc] init];
ABTableViewController_Pad * tableViewController = [[ABTableViewController_Pad alloc] initWithNibName:#"ABTableView"];
master = [[UINavigationController_Pad alloc] initWithRootViewController:tableViewController];
[tableViewController release];
// Dummy blank UIViewcontroller
detail = [[UIViewController alloc] init];
detail.view = [[[UIView alloc] init] autorelease];
[detail.view setBackgroundColor:[UIColor grayColor]];
self.view = [[[UIView alloc] init] autorelease];
self.view.backgroundColor = [UIColor blackColor];
[self positionViews];
[self.view addSubview:navToolbarController.view];
[self.view addSubview:master.view];
[self.view addSubview:detail.view];
}
// Handles the respositioning of view into it's current orientation
-(void)positionViews{
CGFloat tabBarOffset = 0;
if(self.tabBarController){
tabBarOffset = self.tabBarController.tabBar.frame.size.height;
}
if(self.interfaceOrientation == UIInterfaceOrientationPortrait || self.interfaceOrientation == UIInterfaceOrientationPortraitUpsideDown) {
self.view.frame = CGRectMake(0, 0, 768, 1004);
navController.view.frame = CGRectMake(0,0,768,44);
//adjust master view
[master.view setFrame:CGRectMake(0, 44, 320, 1024 - 44 - 20 - tabBarOffset)];
//adjust detail view
[detail.view setFrame:CGRectMake(321,44, 448, 1024 - 44 - 20 - tabBarOffset)];
}
// Landscape Layout
else{
self.view.frame = CGRectMake(0, 0, 748, 1024);
navToolbarController.view.frame = CGRectMake(0,0,1024,44);
//adjust master view
[master.view setFrame:CGRectMake(0, 44, 320, 768 - 44 - 20 - tabBarOffset)];
//adjust detail view
[detail.view setFrame:CGRectMake(321,44, 1024 - 320, 768 - 44 - 20 - tabBarOffset)];
}
}
Well I've found a solution, though I'm still scratching my head as to why it's working.
Basically, I called layoutIfNeeded on my UINavigationController, and that fixed everything right up. What I don't understand is why it was working before, or why I would need to call layoutIfNeeded, as I assumed setFrame would automatically deal with laying out any subviews of controllers.