I have a UITableViewController inside of a UINavigationController.
I want to have a UIView appear over the top of the table view but not susceptible to the scrolling of the table view.
I.e. if the table view is scrolled, the UIView should remain in the same position relative to the screen, rather than relative to the table view. It should appear anchored in a certain position.
What is the best way to achieve this?
EDIT: To clarify, the view should float transparently over the top of the table view.
Many thanks!
I also wanted to have a floating UIView over my tableView.
So, within my RootViewController (which is a UITableViewController), this worked for me
- (void)viewDidLoad {
/* mylabel is a UILabel set in this class */
[self.mylabel setUserInteractionEnabled:NO];
/* navigationController comes from higher up in the navigation chain */
[self.navigationController.view addSubview:self.mylabel];
}
Similar to what Peter said, create a UIView that will contain both the TableView and the subclassed UIView. Such as:
UIView *view = [[UIView alloc] initWithFrame:frame]; // Define frame as you like
[view addSubview:myTableView]; // This is the reference to your tableView
[view addSubview:myAnchoredView]; // This is the reference to your UIView "floating" subclass
You will also need to turn off user interaction for your floating view. I don't know if this will specifically pass the touches to the underlying UIView's or not though:
[myAnchoredView setUserInteractionEnabled:NO];
If this is blocking touches to your tableView, you may need to pass the reference to your tableView to the anchored view at initialization, then pass the touch events along. You can do this by overriding the touch response methods in UIResponder. (If there is a better way, someone please speak up.)
Do you mean the anchored view should appear transparent over the UITableView, or just above, i.e. anchored view uses top 20% of the available space, table view uses the rest?
In any case, create a UIView containing the anchored view and the table view. If you want the anchored view transparent over the table view, it's a bit tricky, because to scroll the table view, touches have to pass through the anchored view.
Add the surrounding view's view controller to the navigation controller instead of just the tableview.
I investigated how UIScrollView keeps its scrollIndicator above the UIScrollView's content and yet unmoving by examining a UIScrollView in the debugger.
The scrollIndicators are UIImageViews. And you can see they are direct descendants of the UIScrollView itself. You can also see that any scrolled content is also a direct descendent. So how is it that the scroll indicators don't move?
I tried updating the position of my static content constantly in - (void)scrollViewDidScroll:(UIScrollView *)scrollView this, surprisingly, works. I'm not sure if it is how UIScrollView itself does it, but without some private magic, it must be something like this.
Related
I have a table view and have attached to its tableHeaderView a UISegmentedControl. How can I make the tableHeaderView fixed so that i can always view the UISegmentedControl in the same position even when i am scrolling the table view?
tableView:ViewForHeaderInSection: is your option to achieve your task. In plain table this will somehow looks like Address Book app with first char of name in section, but you will have your segmented control
I would suggest placing the UISegmentedControl in a separate view on top of the UITableView rather than in the tableHeaderView. You might also want to set yourTable.bounces = NO; in order to keep the header view from bouncing when you get to the top of the table.
First lower the tableview's frame(make space on top of tableview). Then during scrollViewDidscroll, fix the tableviewheader, properly, so it doesn't scroll with it.
Thats the summary of it.
To expand on #mskw's answer:
You can use the UITableViewController (keep the niceties such as UIRefreshControl support and keyboard avoidance). You just have to embed your toolbar in a plain view and place that in your tableHeaderView. Then implement this scroll view delegate method to lock.
#pragma mark - UIScrollViewDelegate
- (void)scrollViewDidScroll:(UIScrollView *)scrollView
{
CGRect rect = self.toolbarContainerView.frame;
rect.origin.y = MIN(0,scrollView.contentOffset.y + scrollView.contentInset.top);
self.toolbarContainerView.frame = rect;
}
Note that if you also use section headers you will have to send those views behind your tableHeaderView otherwise they will float over the tableHeaderView.
I need a UIScrollview to show some static content in my app. I have taken a UIScrollView through XIB and started adding some UIImageViews and textViews in to it. But after coming to the end of the scrollView the view is not exapanding anymore. I need more space so that I can add some more views below. Is there any way in which I can do this (through XIB and not through code).
I struggled a lot to get this done in a more elegant way then the solution described by Csabi.
It's really simple:
In your xib file just add a generic view which is not a subview of your viewController.view (i.e although this view is in your xib file, it is not a part of your viewController's view hierarchy.)
Then select this view and using size inspector set the width and height that suits your need. Add whatever objects you want to this view. Hook up this view to your viewController with an IBOutlet. (Let's call it IBOutlet UIView *myBigView).
in your viewController.view drag a ScrollView and adjust the size of the scroll view as you like. Hook this up to your viewController. (IBOutlet UIScrollView *scrollView)
Now it's super simple:
-(void) viewDidLoad{
[super viewDidLoad];
self.scrollView.contentSize = self.myBigView.bounds.size;
[self.scrollView addSubview:self.myBigView];
}
Yes it is you can define the height of the UIScrollView simply for example height:1800
now you get bigger UIScrollView then your view you can put your objects to scrollview, and if it is filled simply pull it upward and drag and drop other items when you are finished simply pull the scroll view to its position and you have it.
It is simple :)
Hope it helps
Ask if you have any other question
I've got a detail view with various labels and such providing information about a place (address, phone, etc.). The information provided is taller than an iPhone screen so they're all in a UIScrollView (itself inside a UIView) that allows you to swipe up and down to see everything.
I also have an MKMapView inside the scrollview. When it's not attached to anything in Interface Builder it moves up and down with the scrollview, as it should, staying in it's correct relative position to the other controls in the scrollview. You can play with the map, zooming and panning it, etc. and it shows your current location by default.
However, as soon as I hook it to an MKMapView variable in IB, the mapview no longer scrolls with the scrollview. Instead it literally just sits in the position it's originally displayed in (bottom of the view, with a little of the map hidden below the bottom of the view) and the scrollview scrolls up and down behind it.
What's happening here? I've tried changing a bunch of the mapview's and scrollview's properties in IB, but it has no effect. The only thing I haven't tried is just creating the mapview entirely in code, but that doesn't seem like an obvious solution.
EDIT: Sorry to everyone about the expired bounty. I got hung up in other areas of the project and couldn't get back here until now. I didn't know it would expire.
FURTHER EDIT: Well, I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Stupid mistake, sorry to have wasted your time (and the bounty). I'll delete this question after I think the answerers have had a chance to see the result.
Looking like as you are using the ScrollView,you need to scrolling facility in your DetailView.
Instead of using the ScrollView ,I had an alternative of this ....
You can try your hard luck by using the TableView instead of ScrollView.
Just take all the labels and mapView in a single View and then put that view in the header of the TableView.
like this :
UITableView
--> View
------>All Labels // Inside the singleView
------>MKMApView // At bottom of the View
Still You can play with the map, zooming and panning it, etc. and it will show your current location by default.
Hope this alternative can solve your problem.......
All the Best
If hooking up an outlet in IB is breaking an otherwise working view, you might be able to try this to locate the view at runtime:
- (UIView *) findClass:(Class) aClass inView:(UIView *) aSuperview {
for ( UIView *view in aSuperview.subviews ) {
if ( [view isKindOfClass: aClass] ) break;
if ( ( view = [self findClass: aClass inView: aSuperview] ) ) break;
}
return view;
}
- (void) viewDidLoad {
MkMapView *map = [self findClass: [MkMapView class] inView: self.view];
}
I figured out my problem. For reasons completely unknown to me I had
[self.view addSubview:mapView];
in the viewcontoller's ViewDidLoad. Once it was hooked up then that line of code would (obviously) make the map a subview of my of view, effectively yanking it out of the scrollview.
Do you have setContentSize property set to the content's size in the viewDidLoad method of the UIViewController?
I've got a button that I'm adding as a subview of a table view's tableHeaderView. The button appears fine, and tap-and-holding on it works intermittently - for the most part, though, it's unresponsive. I've tried adding it as a subview of the table itself; the effect is about the same. I thought the problem might be with the scroll view's touch interception, but disabling scrolling on the table has no effect either.
Am I doing something wrong? Has anyone else encountered this?
edit - to clarify, I'm talking about the main table header, not a section header, in a grouped-style table; think basically modeled after the "Contact" screen.
I had the same problem. In my case I had a container view instantiated in IB (that was applied as the table view header in code), with a UIImageView occupying the entire frame of that container. The misbehaving button resided in the image view.
Turns out I needed to have sizing struts in IB set as follows...
Container View: exterior anchors all on, interior resizing - all off
Sub Image View: all struts on (interior and exterior)
I had several different table views, all using header views. Some would respond to touch events correctly, some were flaky. This solved my problem
I had a similar problem - a textfield and button inside a view set as the table header view which would not respond to touch events. setAutoResizing programmatically worked for me.
My controller extends UITableViewController, and viewDidLoad looks like this:
- (void)viewDidLoad
{
[super viewDidLoad];
MYCustomWidget *headerView = [[[NSBundle mainBundle]
loadNibNamed:#"MYCustomWidgetView" owner:self options:nil]
objectAtIndex:0];
[headerView setAutoresizingMask:UIViewAutoresizingNone];
self.tableView.tableHeaderView = headerView;
}
('MYCustomWidget' extends UIView, and 'MYCustomWidgetView' is a XIB file).
I completely disagree with Wisequark -- there's absolutely nothing wrong with putting a button in the tableHeaderView, and including one would not risk your app being rejected from the app store. The tableHeaderView is designed to be an arbitrary view containing whatever elements you choose.
As far as your issue, it could be that you've got a view obscuring your button, or, it may simply be a bug that should be reported to Apple.
Strangely enough, but the table header view is apparently resized incorrectly.
I use auto layout, so autoresizing mask was not an option for me. After inspecting my view hierarchy:
and noticed that my custom header view had incorrect height, so only less then half of it was tappable (see highlighted view):
Manual updating of its height fixed the problem:
- (void)viewDidLayoutSubviews {
CGRect frame = self.tableView.tableHeaderView.frame;
frame.size.height = 116.0;
self.tableView.tableHeaderView.frame = frame;
}
Also, the table view header height can become invalid after the orientation is changed. This problem also can be fixed with the provided solution.
My situation was similar to Danny Hall's (the table header view was a UIImageView, and there was a UIButton which was a subview of the UIImageView). In my case, the problem appears to have been caused by the button being a subview of the image view. What worked for me was creating a UIView "containing" view, such that both the image view as well as the button were subviews of the "containing" view. strange.
tableHeaderView has 0 height while it is processing draw in UITableView
use this UIView subclass to set the strong constant height and ignore UITableView processing
#import <UIKit/UIKit.h>
#interface CustomHeaderCell : UIView
#end
//-----
#import "CustomHeaderCell.h"
#implementation CustomHeaderCell
-(void)setFrame:(CGRect)frame {
frame.size.height = 43; // !!! constant height
[super setFrame:frame];
}
#end
I have the same problem UIButtons actions not working in UITableView's header view. First i tried setAutoresizingMask to .None which not works then after reading the answers of #Davyd and #Alexey i realise that i did not set the height of headers view then i set it like:-
self.tablevwMain.tableHeaderView?.frame = CGRect(x: 0, y: 0, width: width_view, height: your_height)
And all UIButton'sinside UITableView's header view works correctly.
For me UIControl like UIButtons on headers only worked if I add it to the cell's contentView
addSubview(stackView) //Does not work
contentView.addSubview(stackView) //Works
Don't forget to set the footer height in:
-(CGFloat)tableView:(UITableView *)tableView heightForFooterInSection:(NSInteger)section{
You should consider that this is not the intended sue of the headerView and that an implementation such as that might result in rejection from the AppStore as a result of a HIG violation. Given that the dimensions of a header are intended to be small, it is probably better to consider a restructuring of your view. Having said that, there is no easy way to do it short of hand detecting touch events and determining the geometry yourself, then executing a selector based on the geometry - in short, rolling your own button class.
I have just started learning objective-C and the iphone sdk and I have a question that I hope someone can help shed some light on.
What is the difference in the following:
self.view = someView;
and
[self.view addSubView: someView];
Say for example, in a simple app, where we have only one controller and one container view (has a few image subviews).
What is the difference between the two statements? The reason that I'm asking is because I was tinkering around in some sample code and I noticed the view was being initialized with images as subviews like so:
if (self = [super initWithFrame:CGRectZero])
{
//adds some images as subviews here
}
As I understand it the initWithFrame: CGRectZero, creates a frame with size at [0,0,0,0] (essentially invisible).
When I directly set the view with
self.view = someView;
I notice the view actually displays the image. But when I add the view to as a subview of controller's 'default' view, it doesn't. So basically my question is, whats going on behind the scenes? Why is the first method "resizing" the frame and the second one not doing the same thing.
What you see on the screen of your iPhone is almost always a hierarchy of views.
When you look at, say, your inbox in Mail, you're seeing a bunch of views. There's a big containing view.[1] Within that, there's a navigation bar view, a table view, and a toolbar view. Within the navigation bar view, there's a button view on each side and a label view in the middle. Inside the table view, there are a bunch of table cell views, and each of those cells has several label views. The toolbar has five button views. I could go further and talk about the views inside those buttons and so on, but I'm sure you get the idea.
The view above any given view is its superview; the views below it are its subviews. So a table cell view has a table view as its superview and a bunch of label views as its subviews. The top view, the one that has all the other views inside it, is called the root view.
Each view has its own drawing surface. The rectangle formed by that drawing surface is called the frame. The frame of a view is relative to the frame of its containing view. So if one of our table cell's label subviews has its frame at (0,0), that means it will be in the table cell's top left corner, even if the cell is halfway down the screen.
When you're writing a view controller, self.view is that root view I mentioned earlier; all the other views are subviews of that one (or subviews of its subviews, etc.). One of the features of a view controller is that it automatically resizes its self.view to fit the screen. (The available area will be smaller in the middle of a phone call: the status bar is twice as high then, so there's less space for your app. It will also be smaller if your view controller is being managed by a navigation controller or tab bar controller, but that's a different story.) But just because you resize its root view doesn't mean that the root view's subviews will automatically resize. To do that, you need to set their autoresizing mask (a property which tells the view how it should react when its superview changes size):
someView.autoresizingMask = UIViewAutoresizingFlexibleWidth
| UIViewAutoresizingFlexibleHeight;
(There's a graphical way to set up the autoresizing mask in Interface Builder—click the ruler icon in the inspector window and look at the "Autosizing" section.)
Even that's not enough, though, if someView isn't the right size to start with. To do that, adjust its frame before you add it as a subview of self.view:
someView.frame = CGRectMake(
0, // all the way to the left
0, // all the way at the top
self.view.frame.size.width, // same width as the root view
self.view.frame.size.height, // same height too
);
So why would you ever use subviews if you have to do all this twiddling that the root view does for you? Simple: you can only have one root view, but one view is almost never enough for what you need to do. If you really need only one view, of course, you can just set it as the root view and go on your merry way, but chances are, things are more complicated than that.
[1] I'm simplifying a bit here, but that's fine for right now.
When you add a view as a subview, you need to make sure that you're actually adding to an existing view.
self.view = view sets the controller's view. Without this (either in code or done with a XIB) you'll never see anything as the controller has no view to show.
[self.view addSubView: someView] assumes that self.view is already set. If it doesn't, you're adding someview as a subview of nil, and it will never get seen.
Basically, think of self.view as the big container, and all the subviews are just pieces inside of it. If you don't need any subviews, setting self.view to a UIImageView or UIWebView is fine. If you do need subviews, you'll need a big, empty container view in which to put them.
In your case, I'm betting self.view is never set, and you're adding your image views to nil.
Setting the view controller "view" property only changes the view it is managing.
Adding a view as a subview of another view, actually adds the subview underneath the other view.
They are very different things, as one adjusts a view controller and the other alters a view hierarchy.
As a guess, the reason you didn't see anything the first way was the frame for the subview you were adding was CGRectZero (0 in size).