Pin Footer/View to bottom of screen programmatically with AutoLayout - ios

I have a UITableView, but I would like an ever present UIView at the bottom of the TableViewController screen.
I want to use AutoLayout programmatically to pin this view to the bottom and sides of the screen, because it needs to work with multiple screen sizes.
I ran into problems trying to add a footer view to the UITableView because I wasn't able to pin it to the bottom of the screen (edge cases where there is only 1 item in the UITableView would render the footer in the middle of the screen.
Is there any way to do that well?
I've started with this, but I'm stuck trying to figure out what to put for the frame since the frame will change based on screen size:
let bottomView = UIView()
bottomView.backgroundColor = .black
view.addSubview(bottomView)

Related

Center a scrollable InputAccessoryView

All,
I have a scrollable InputAccessoryView attached to my UITextView.
This was created as follows:
create a UIScrollView
add a horizontal UIStackView.
Attach top, bottom, trailing, leading of stack view to scroll view.
Set equal heights between stack and scroll.
Insert lots of buttons into stack view.
set TextView.inputaccessoryview = scrollview.
Voila! (Note - only the last line was programmatic).
So this works fine and correctly scrolls when the content (stack view) is wider than keyboard, BUT when you rotate to landscape (or run on an iPad), it is left aligned.
I'd like the buttons to be centred when the keyboard is wider than the set of buttons.
I've tried embedding the Scrollview into a UIView with Center X, but that doesn't seem to work.
Can anyone give me some pointers?
Thx
Found it!
The answer is to use ContentInset on the scrollbar (which is the InputAccessoryView above). The specific code is:
public void CentreToolbar()
{
var offsetX = Math.Max((scrollView.Bounds.Width - scrollView.ContentSize.Width) / 2, 0);
scrollView.ContentInset = new UIEdgeInsets(0, (nfloat)offsetX, 0, 0);
}
This code should be called anytime the layout of the screen changes - such as during rotation.

Unwanted white space on left of UIScrollView on iPhone X

UIScrollView works fine without the white space on the left on all iPads or iPhones except for iPhone X. How can I remove the white space?
I use storyboards. Bounce On Scroll/Zoom are all disabled. No white space on iPad or iPhone except for iPhone X. I think it might be something related to the Safe Area thing.
This spacing is from safe area, which is applied to left/right of UIScrollview as content insets in landscape orientation on iPhone X, which can be seen using read-only property UIScrollview.safeAreaInsets.
Following line can be used to get rid of safe area insets when you dont need:
UIScrollview.contentInsetAdjustmentBehavior = .never
The default value being UIScrollViewContentInsetAdjustmentBehavior.automatic includes safe area layout guide margins as content insets.
Note: auto layout constraints has nothing to do with the insets, its just iOS 11 UIScrollview content insets adjustment behavior.
Setting the constraint relative to safeArea is good practise for iPhone-X.
This is how apple says -
When the view is visible onscreen, this guide reflects the portion of
the view that is not covered by navigation bars, tab bars, toolbars,
and other ancestor views.
In your case you are giving constraints leading & trailing of scrollView with safeArea, Not superView
Hence if you take risk giving constraint to superview instead of safeArea your object content may clipped, specially when you rotate left, content from the left most will clip under top notch of iPhone-X.
Apple doc for safeAreaLayoutGuide
Well, I solved this issue in non-elegant way. But it works like a charm. (I tried all other answers. Thank for your help, however those answers don't seem to work in my case.)
var leftMargin: CGFloat = 0
var rightMargin: CGFloat = 0
if Device.isPhone() && Device.IS_5_8_INCHES() {
self.leftMargin = 44
self.rightMargin = 44
}
let frame = CGRect(
x: (self.view.frame.width - self.leftMargin - self.rightMargin) * CGFloat(pageIndex),
y: ...
)
Safe Area Layout is responsible for this white space.
1st Option:
Ignore safe area layout for your scrollview and set scrollview's constraints with respect to its super view (or main view). Scrollview automatically handle safe area inset for contents while scrolling.
Landscape View:
Portrait View:
2nd Option:
I do not recommend to remove/change safe area layout for your scroll view and an alternate solution that can solve white space visibility issue:
Set blue background color (that you've applied for your scroll view) to your main view of your view controller, if scroll view is covering entire screen.
set clear color background for your scroll view
Add this code in your viewDidLoad
# IBOutlet var scrollView: UIScrollView!
override func viewDidLoad(){
super.viewDidLoad()
self.view.backgroundColor = UIColor.yellow // set here blue color that you've applied for your scroll view
self.scrollView.backgroundColor = UIColor.clear
}
Here are good reference answers regarding Safe Area Layout, for better understanding:
Safe Area of Xcode 9
Use Safe Area Layout programmatically

View not reaching the bottom of the device screen using Autoresize Subviews

I have a view controller that has a button at the top center, and a view underneath which will have the collectionview inside. I want this collectionview to scroll separately to the top view (so it scrolls underneath that top section).
This works fine but the view with the collectionview doesn't reach the bottom. You can see in the screenshot I gave the superview a red background - the collectionview doesn't reach to the bottom of the view. All the constraints are fine.
It works if I uncheck "Autoresize subviews" in the Interface Builder for the UIView inside the View Controller but this then makes all the cells and any navigation bars I add inside that view 1000px wide. Why is my view being shortened - and how can I get the best of both these scenarios? A View that hits the bottom of the screen and all the cells/navigation bars the width of the view?
Constraints for the view:
Constraints for the collectionview:
I did have a tab bar too, and the gap is the exact height of my tab bar.
Anyway I fixed this by putting:
self.extendedLayoutIncludesOpaqueBars = YES;
self.edgesForExtendedLayout = UIRectEdgeBottom;
in viewDidLoad method.

Dynamic UILabel size for view before uitableview

I am having abit of trouble here trying to make this post description label to grow and shrink based on content size on IOS9. I have a view (I will refer it topView) that I am using as a header for the tableview (So when I scroll up the header disappears). Inside the topView, there are a bunch of stack views. I wish to grow and shrink the post description label in height based on content size. I do know how to do it in simple case where everything is inside the prototype cell (i.e. set estimated row height and set uitableviewautomaticDimensions, set sizetoFit on label and change number of lines to 0). However, this is a different case because the post description label is not really inside the cell, it is in its view before the table view cells.
Note that all items in the view has static height except the postdescription label. Post description label is inside a stack view that is pinned only left and right (So that top and bottom would grow?). Also, the main stack view that contains all elements is pinned towards the four sides with the topview that contains the main stack view also pinned towards the four sides. With this setup, I would expect the topview to grow and shrink based on the content size. However, I do not see that in the output. I dont know if it is the stackview that is holding the label refusing to grow or the top view refusing to grow to allow more space for the stackview for the label. Thanks
UPDATE
Thanks Riadluke, I tried doing something as suggested which is resizing the headerview after calculating the required height. I have placed the following code in viewDidLayoutSubview and it works with an issue
postDescriptionLbl.sizeToFit()
let headerView = commentTableView.tableHeaderView!
headerView.setNeedsLayout()
headerView.layoutIfNeeded()
let height = TopStackView.frame.size.height + ImageStackView.frame.size.height + postDescriptionLbl.frame.size.height + SpacerStackView.frame.size.height + BottomStackView.frame.size.height
headerView.frame.size.height = height
commentTableView.tableHeaderView = headerView
The issue I now have with this method is that when the view controller appears, I can physically see the postDescription label height grow from the default height in storyboard to the required height. For example, when the VC first appears, I see a line of label with some string being cut off, however after 0.5 second, the headerview and the label grow to the size that I wanted. I know this would be expected because I was calling the manipulation after the viewDidLayout Subview. I was wondering if there is a better way such that I dont see that transition and the view appears to be the right height straight away. Ie. let the view know exactly how high the label is to determine how high the headerview needs to be before appearing on screen?
I'm afraid the view set as tableHeaderView of a UITableView does not get resized automatically. Its height will be fixed to the height it had in IB.
What you have to do is set its size manually and then reassign it as the tableHeaderView so it is displayed in the height you want.
It could take only few lines since you're using autolayout.
You can try this code right after you've set the header view's contents:
//for the target size you have set the width as your tableView's width when it is already displayed on screen.
//note that when it is accesed inside viewDidLoad the tableView's bounds
//may be different to the actual bounds it will be displayed with,
//here I am just using the screen bounds
let targetSize = CGSize(width: UIScreen.mainScreen().bounds.width, height: 10000)
//set the tableHeader's size to its size after its layout constraints are resolved
tableHeader.bounds.size = tableHeader.systemLayoutSizeFittingSize(targetSize)
//reassign it as the tableHeaderView to update the height it will be displayed in
tableView.tableHeaderView = tableHeader
After many many attempts, I could not get anything to work with the original setup. The best I achieved was to resize it after view did appear which is not idea as you see the previous layout.
It is now working with a complete different approach. I have created two prototype cell and have one as "HeaderViewCell" and implemented the following functions
commentTableView.estimatedSectionHeaderHeight = 400
commentTableView.sectionHeaderHeight = UITableViewAutomaticDimension
func tableView(tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
Everything works like a charm after that.

Button over tableviewcontroller stretching (Swift)?

I have a button in a view which is in the footer of a tableview (UITableViewController). Why is the button stretching when I try to apply the following code to it?
And I apply the code:
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
// Make footerview so it fill up size of the screen
// The button is aligned to bottom of the footerview
// using autolayout constraints
self.tableView.tableFooterView = nil
self.footerView.frame = CGRectMake(0, 0, self.view.frame.size.width, self.tableView.frame.size.height - self.tableView.contentSize.height - self.footerView.frame.size.height)
self.tableView.tableFooterView = self.footerView
}
I was following the answer on this question:
Add button on top of UITableViewController (Swift)
Thanks!
If you read the question (whose link you posted) carefully, you can see that he resizes the view to take up the rest of the screen that is remaining after your table. Your button is the same size as your view, that is why it is stretching up. You need to add constraints which bind your button to the bottom of the view but not the top. Because if you bind the top and the bottom both to the view it will stretch.
Here is the example.
1. When you do not bind the button to the top. Notice that in the constraints, I do not have any constraint that specify the top of the button.
When you bind your button to the the top of the view. In this, I set a constraint which bind the button top to the view top. It stretches my button to take up the whole space as the view(which is similar to your case)
Hope this helps. :)

Resources