Hi Is it possible to combine to combine a UIView with a UITableView and reference it as a single object? I currently have a both combined as a Stack, but I feel there is a better way to do it. Thanks
They are two different objects, one property cannot reference two different views. You can however use an IBOutlet for both views in your ViewController, or within any ancestor view.
class MyViewController : UIViewController {
#IBOutlet var tableView : UITableView!
}
Then, you can just drag a reference outlet for that specific view from Storyboard to the view controller. For information on that:
https://developer.apple.com/library/ios/recipes/xcode_help-IB_connections/chapters/CreatingOutlet.html
Note that I did not add a property for your UIView because I assumed it is already the main view of the controller. Similarly, you can subclass your main view, add an outlet for the table view, and use that as the reference outlet.
In my opinion, there are two way to resolve your require:
Option 1. use table view header view. I suppose you want to combine a view named topView with a table view. call tableView.tableHeaderView = topView
Option 2. add topView to table view as a subview, then adjust contentInset of table view. Just like:
UIEdgeInsets tableViewInsets = self.tableView.contentInset;
tableViewInsets.top = topView.frame.size.height;
self.tableView.contentInset = tableViewInsets;
Related
I have UITableViewController, on top of it I placed an UIView and inside it there is a UICollectionView.
When collectionView data is empty I want to remove the UIView. I tried:
self.collectionView.removeFromSuperview()
and
self.collectionView.hidden = true
These removes the UIView but there is an empty space above my tableview. How can I get rid of it?
Edit: added a photo of my storyboard. Recommended View is not inside the tableviewheader.
It looks like the collection view is a table header view (the way you have positioned it in the storyboard).
Try setting it to nil if data is empty
self.tableView.tableHeaderView = nil
I haven't tried but this code should solve your problem.
self.tableView.tableHeaderView?.isHidden = true
You have placed Recommended View inside the table view - not on top of it. So you could have placed it as header or cell. So when you don't want it to be present, change cell pr header height.
The RecommendedView is a tableHeaderView. Try to have a outlet to the height constraint of the RecommendedView.
Set the height constraint to 0 in case you don't have any element in the collection view.
#IBOutlet var tableHeaderHeighConstraint: NSLayoutConstraint!
override func viewDidLoad(){
if collectionIsEmpty(){
tableHeaderHeighConstraint.constant = 0
self.tableView.tableHeaderView?.layoutIfNeeded()
}
}
How to make a UIStackView re-distribute it's sub-UITableViews while the stackView is inside a scrollview?
My layout hierarchy is based on the official documentation from apple about Dynamic content for StackViews
- UISCrollView
- UIStackView
- UIView A
- UIView B
- UIView C
- UITableView X
- UITableView Y
- UIView D
The constraints are set as documented. The initial layout of the StackView is correct showing all visible subviews. When forcing the regular views to expand beyond the screen's height, scrolling is working as expected. Also when viewing the layout in the storyboard, everything stacks as expected.
At this point the UITableViews are empty. As soon as I add content to the tableView the problem appears.
The problem
When I dynamically update the TableView's by calling .reloadData() on both of them I see their content appearing. (thanks to this answer about non-scrolling tableViews) but the UIStackView is not stacking the UITableViews.
UIView D is stacked below UIView C
UITableView X and UITableView Y also stacked below UIView B
My guess is that I need to invalidate the stackview, or somehow get it to redistribute it's subviews. How can I do this?
First, a warning:
What you're trying to achieve is not really standard iOS behavior. You should first consider a different approach like creating a single grouped table view with multiple sections. You can implement custom views inside your table view as section headers or footers.
Now if you really wanna go with your original approach...
... for some important reason you should be aware that a table view doesn't have an intrinsic content size by default. Thus, you need to tell the table view how tall it should be because otherwise it will only shrink down to a zero height.
You can achieve this by either subclassing UITableView and overriding its intrinsicContentSize() as Rob suggests in this answer to a similar question.
Or you add a height constraint to each of your table views and set their constants dynamically in code. A quick example:
Add both your table views to a vertical stack view in Interface Builder.
Give both table views a leading and a trailing constraint to pin their left and right edges to the stack view.
Create outlets for your table views in the respective view controller:
#IBOutlet weak var tableView1: UITableView!
#IBOutlet weak var tableView2: UITableView!
#IBOutlet weak var tableView1HeightConstraint: NSLayoutConstraint!
#IBOutlet weak var tableView2HeightConstraint: NSLayoutConstraint!
Override the updateViewConstraints() method of that view controller:
override func updateViewConstraints() {
super.updateViewConstraints()
tableView1HeightConstraint.constant = tableView1.contentSize.height
tableView2HeightConstraint.constant = tableView2.contentSize.height
}
Now whenever the content of any of your table views changes (e.g. when you add or remove rows or change the cell contents) you need to tell your view controller that it needs to update its constraints. Let's say you have a button that adds a cell to tableView1 each time you tap it. You might implement its action like this:
#IBAction func buttonTappen(sender: AnyObject) {
// custom method you implement somewhere else in your view controller
addRowToTableView1DataSource()
// reload table view with the updated data source
tableView1.reloadData()
// triggers an updateViewConstraints() call
view.setNeedsUpdateConstraints()
}
tl;dr:
A UITableView isn't intended for use without scrolling enabled and thus you always need to explicitly set its height when its contents change - may it be using constraints or by overriding the table view's intrinsic content size.
I want to use
-preferredLayoutAttributesFittingAttributes inside the UICollectionviewCell subclass we are writing.
Currently we already had a weak pointer to out parentViewController and I try to set the collectionViewcell frame width to be the same as parentViewcontroller.view.frame.size.width.
I don't want to have a pointer to the parentViewController from the CollectionViewCell subclass as having a reference to the parentviewcontroller is a bad idea.
My question is....
Is there a simple and ideal way to get the frame of collectionviewcell's super view from inside the collectionViewcell class?
If you want to be use the parent view then you don't need any reference. You can do like this:
CGFloat width = self.superview.frame.size.width;
And as #Ralfonso pointed out in the comments:
Also, if you want to get the superview size immediately after being added to a view hierarchy, override didMoveToSuperview: and access the superview property. If it's nil, the view has been removed from the superview. If non-nil, the view has been added to the superview's hierarchy.
I am trying to add UIView *separatorView at the top of my collection views and table views which appears when the respective view's contentOffset.y > 0. I have made this work on my collection view by adding the view in viewDidLayoutSubviews and added it to self.view. Then by using some of the scroll view delegate methods I can make it show and hide. This works well but for the table view, if I use the same concept the separatorView scrolls with the table view. I am still adding the separatorView to self.view but it behaves differently than on the collection view. I don't want the separatorView to scroll. I want it to stay at the top. Am I correct in believing that the way to make this work for the table view is to subclass UIViewController and manage a table view within that along with the separatorView? Is there any way to make it work in my current UITableViewController subclass?
Basically, why does self.view scroll with the table view and not with the collection view? This seems inconsistent.
Thanks for the help.
Edit: Added picture to demonstrate idea.
self.view in a UITableViewController is a UITableView because the tableViewController overrides loadView on the view controller.
If you run the following code:
UITableViewController *tableViewController = [[UITableViewController alloc] init];
NSLog(#"%#",tableViewController.view);
You would get the following console output:
2014-05-13 21:19:02.823 Test[28681:60b] <UITableView: 0x109834200; frame = (0 20; 320 548); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = <NSArray: 0x109548f00>; layer = <CALayer: 0x109548c40>; contentOffset: {0, 0}>
As you can see, self.view is not a UIView but instead it is the UITableView. Your separator view scrolls with the tableView because calling:
[self.view addSubview:separatorView];
is exactly the same as calling:
[self.tableView addSubview:separatorView];
If you wanted the separator to stay in a fixed position it would need to be added to the tableView/collectionView's superview and not the scrolling view itself.
The reason this is not happening in the UICollectionViewController is because self.view is actually a UICollectionViewControllerWrapperView object. This is a private class but judging by its name, I am assuming it is a UIView that wraps the UICollectionView.
The most sensible way to achieve what you want would be to use a custom container view (child view controller) that would be a UIViewController subclass containing your separator view and tableView/collectionView controller.
If you didn't want to add a child view controller then the other alternative would be to like you said, create a UIViewController subclass and add your separator view and tableView as a subview within the view controllers view however if you don't use a UITableViewController you lose bonus functionality like automatically adjusting the tableView's contentInsets for keyboard appearance etc.
I've got a UITableView that I'd like to stick a 44px subview on top of. I tried tableViewHeader, but that scrolls with the rest of the table.
I tried searching and have found many people saying I need to add a UIView superview and then add my header and the UITableView to it. However I can't find an example on exactly how to do this. I tried making a new UIView subclass and laying out the subviews in IB, but I ran into trouble getting the table controller to link w/ the UITable (because I don't know enough about IB).
How can I do this with XIBs? Can someone provide an example?
Thanks for any help you guys can provide.
I finally figured this out right after posting. Figures. :)
Here's what I did, in case others run into the same problem:
Delete the existing UITableViewController and its XIB. They're junk. Get really mad while you do.
Make a new UIViewController subclass with a XIB
Open XIB in IB and add your header stuff and a UITableView to the UIView
In the IB Outlets for UITableView make sure you connect Delegate and DataSource to your File Owner
In the header for your view controller, be sure to add <UITableViewDelegate, UITableViewDataSource> to implement these protocols
Implement all the regular UITableView delegate and data source methods you know and love, but in your UIViewController instead of the way you're used to doing it through UITableViewController
After this things should work.
The problem is, UITableViewController's view property is the same thing as the tableView property. I had the same problem, wanting to put some fixed content above the table. I didn't want to change the base class, as it provides lots of great functionality I didn't want to lose or disrupt.
The fix is actually easy. The trick is to create custom set and get for self.tableView property. Then, in loadView, you replace the view with a fresh UIView and add the tableView to it. Then you're free to add subviews around the tableView. Here's how it's done:
In header:
#interface CustomTableViewController : UITableViewController
{
UITableView *tableView;
}
In .m:
- (UITableView*)tableView
{
return tableView;
}
- (void)setTableView:(UITableView *)newTableView
{
if ( newTableView != tableView )
{
[tableView release];
tableView = [newTableView retain];
}
}
- (void)loadView {
[super loadView];
//save current tableview, then replace view with a regular uiview
self.tableView = (UITableView*)self.view;
UIView *replacementView = [[UIView alloc] initWithFrame:self.tableView.frame];
self.view = replacementView;
[replacementView release];
[self.view addSubview:self.tableView];
//code below adds some custom stuff above the table
UIView *customHeader = [[UIView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, 20)];
customHeader.backgroundColor = [UIColor redColor];
[self.view addSubview:customHeader];
[customHeader release];
self.tableView.frame = CGRectMake(0, customHeader.frame.size.height, self.view.frame.size.width, self.view.frame.size.height - customHeader.frame.size.height);
}
Enjoy!
Replace the TableViewController with a ViewController, inside it add a UIView with fixed height to place the fixed content you need, and below that add a UITableView.
Create an outlet to your TableView
#IBOutlet weak var tableView: UITableView!
You can reuse all the funcs you already have removing the override word for example
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return data.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCellWithIdentifier(cellId) as UITableViewCell!
c.textLabel?.text = "A title"
c.detailTextLabel?.text = "A subtitle"
return c
}
See this answer to add the automatic refresh control if you need it Pull to refresh UITableView without UITableViewController
As an option it is possible to embed UITableViewController as part of UI into another UIViewController with 'Container View' element (pick one in Interface Builder from the Object library (where all other views are) ).
This way you can use UITableViewController like ordinary view (in terms of positioning) and compose any layout you need without overwritting existing table view code
EDIT:
to further expand my answer, here are the steps to accomplish the described approach:
Open you storyboar in interface builder
Drag'n'drop a new ViewController element to the storyboard from Object Library to add a new controller.
As a child view, drag'n'drop Container View from Object Library and place it anywhere inside the ViewController
Container View creates another view controller and "embedded" segue as a connection. It's save to delete this viewcontroller and to connect the Container View with the required view controller (as per the questions it's UITableViewController)
To connect Container View with UITableViewController just select the container view and control+drag to the UITableViewController - select "Embed" in the popup menu
Now the controller will display inside the Container View with respect to the container's position and boundaries.
It's possible to get a link to the embeeded view controller - the system will fire "prepareForSegue" method of the host viewcontroller (created on the step 1 above) and the controller is in segue.destinationViewController property - one can customize it as required. Just make sure to set an identifier to the "embedded" segue in interface builder - this is the same process just like for any other segues.
Define a custom UIView in storyboard or xib, have a IBOutlet reference for that UIView in View Controller. In -(void)viewWillAppear:(BOOL)animated method write [self.tableView.superview addSubview:filterHeaderView];, here filterHeaderView is the IBOutlet reference for my header view which I want to add as fixed header in my tableview.