I have a table view which displays table view cells and a header view (search bar). I want the width of the table view cells to be 0.9 of the device screen size, and the header view to have the same size. However, this is not possible because cells and the header view are all contained in a table view.
When I use layout anchor constraints everything gets resized (including the header view).
self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
// here's the 0.9 multiplier
self.tableView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.9).isActive = true
self.tableView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
How can I make sure that the table view header has the same width of the view controller, while keeping the table view width to be 0.9 of the main view?
It is best not to try to adjust the constraints of a TableViewCell or its contentView. However, you can just set both those views to have a background color of .clear and then add subviews to the contentView with the constraints you want.
class CustomCell: UITableViewCell {
let smallerView = UIView()
override init(frame: CGRect) {
super.init(frame)
smallerView.translatesAutoresizingMaskIntoConstraints = false
contentView.addSubview(smallerView)
smallerView.widthAnchor.contraint(equalTo: contentView.widthAnchor, multiplier: 0.9).isActive = true
smallerView.heightAnchor.contraint(equalTo: contentView.heightAnchor).isActive = true
}
}
Your custom cell "starts with" a ContentView, which uses uses the table view's insets to set its width...
So, if you have a bunch of elements / objects that you are adding to the content view...
Your best bet is to probably first add a standard UIView as a "container view"... (just my naming of it)...
Then, set the constraints on that "containing view" to be 0.9 of the width of the contentView...
All of the elements you add to that "containing view" will be constrained relative to it and thus will all stay inside the 0.9-width overall...
Related
I have a UITable view in my View controller and a stackview at the bottom of the table view. I want the UITable view to go full view when stackview isHidden. Here is the code i write to do so :
if response.total != 0.0{
checkoutStackView.layoutIfNeeded()
medDetailTableView.translatesAutoresizingMaskIntoConstraints = false
medDetailTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0).isActive = true
vwPlaceOrder.isHidden = true
viewCheckout.isHidden = true
checkoutStackView.isHidden = true
}else{
checkoutStackView.layoutIfNeeded()
viewCheckout.isHidden = false
checkoutStackView.isHidden = false
self.vwPlaceOrder.isHidden = false
self.lblTotalItemsPrice.text = String(format: "%.1f",response.total)
medDetailTableView.translatesAutoresizingMaskIntoConstraints = false
medDetailTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -70.0).isActive = true
}
But this code is only showing the stackview but when the stackview is hidden UITableview is not going full view.
Here is a image when stackview is showing:
And here is the image when stackview is hidden:
Try to use footer view of UITableView for placing your view. And make UITableView full size of it's superview.
It should solve your problem.
When you hide a view in Swift , it just make it hide but constraints of hiden view is still alive. So you must to set 0 this height constraints.
For this you can create an outlet of Constraints and change it like :
#IBOutlet weak var heightConstt: NSLayoutConstraint!
And when you hide stackView change its constraint
heightConstt.constant = 0
UIScrollView lets us set a scrollIndicatorInset. However, when setting a bottom inset for this value on a landscape iPhone with a safeAreaInset (i.e. a 'notch'), the horizontal position of the scrollbar is unexpectedly updated.
Here is a scroll view on an iPhone X with no changes to scrollIndicatorInset - note that the scrollbar on the right edge is horizontally flush with the edge of the screen.
Now I add one line of code:
scrollView.scrollIndicatorInsets.bottom = 1
The bottom edge of the scroll indicator is inset as expected. But the scrollbar is now also relocated horizontally to align with the safe area, rather than the screen edge.
What is the cause of this horizontal inset and how can I prevent it?
Setting the bottom scroll inset to anything other than 0 adds the additional margin, and setting it back to 0 removes it. The same thing applies when setting any of the scroll insets edges.
Some logging before and after setting the scrollIndicatorInset shows no change to the safeAreaInset nor the layoutMargins on the view, the scroll view, or the content view inside the scroll view.
One place this is a problem is when placing a text field inside a scroll view and adjusting the bottom inset to accommodate the keyboard. The scrollbar jumps around as the keyboard is presented and dismissed.
I am providing a small view controller below in case you want to try it out for yourself.
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let scrollView = UIScrollView()
scrollView.backgroundColor = .white
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
let contentView = UIView()
contentView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contentView)
contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalToConstant: 50).isActive = true
contentView.heightAnchor.constraint(equalToConstant: 500).isActive = true
// this causes the issue
scrollView.scrollIndicatorInsets.bottom = 1
}
}
This is an old question, but as a workaround you can also set, in your case, the scroll view's right indicator inset to an appropriate negative distance, such as the parent view's right safe area inset.
override func viewDidAppear(_ animated: Bool) {
scrollView.verticalScrollIndicatorInsets.right = -self.view.safeAreaInsets.right
}
In iOS 13 and above, we should be using horizontalScrollIndicatorInsets and verticalScrollIndicatorInsets as scrollIndicatorInsets has been deprecated.
This isn't a perfect workaround, as you'll find the top and bottom insets also need adjusting. For instance, maybe you're trying to set the bottom inset to end halfway up the screen, and you're happy with that. But after these adjustments the top inset will now be zero by default, so you'll probably want to adjust that, too, or the scroll indicator will go all the way up into the rounded corner of the device.
I have a view that is composed of an image, a form with 11 UITextfield and a button, but the form is too big for my screen that is why I tried to use a UIScrollview.
The error I have is that my UIScrollview does not work as I can solve this problem.
This is my code:
import UIKit
class LoginCtrl: UIViewController {
let scrollView: UIScrollView = {
let sv = UIScrollView(frame: UIScreen.main.bounds)
sv.isScrollEnabled = true
sv.contentSize = CGSize(width: 2000, height: 5678)
sv.translatesAutoresizingMaskIntoConstraints = false
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(r: 0, g: 150, b: 136)
self.view.addSubview(scrollView)
scrollView.addSubview(contenedorCampos)
setear_posicion_scrollView()
setear_posicion_contenedor()
}
func setear_posicion_scrollView(){
//definir x,y,width,height constraints
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
}
var heightContenedor: NSLayoutConstraint?
func setear_posicion_contenedor(){
//definir x,y,width,height constraints
contenedorCampos.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
contenedorCampos.topAnchor.constraint(equalTo: tabsInicio.bottomAnchor, constant: 12).isActive = true
contenedorCampos.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -24).isActive = true
heightContenedor = contenedorCampos.heightAnchor.constraint(equalToConstant: 400)
heightContenedor?.isActive = true
contenedorCampos.addSubview(txtNombres)
contenedorCampos.addSubview(divider_txtNombres)....
}
}
Thanks
You've defined the relationship between the scrollview and its superview(which defines its frame), but not the relationship between the scrollview and its subviews (which defines its contentSize). As a result, the actual contentSize of the scrollview will be just be (0, 0).
In other words, you never actually laid anything out, at least not in the code you posted.
What you need to do is define layout constraints for the actual child views (everything that is a subview of the scrollview). Make sure to set up constraints definitively pinning these components to the edges of their parent (the scrollview). Once you have defined the constraints sufficiently, the scrollview should have a content size.
Technical note about this
In general, Auto Layout considers the top, left, bottom, and right
edges of a view to be the visible edges. That is, if you pin a view to
the left edge of its superview, you’re really pinning it to the
minimum x-value of the superview’s bounds. Changing the bounds origin
of the superview does not change the position of the view.
The UIScrollView class scrolls its content by changing the origin of
its bounds. To make this work with Auto Layout, the top, left, bottom,
and right edges within a scroll view now mean the edges of its content
view.
The constraints on the subviews of the scroll view must result in a
size to fill, which is then interpreted as the content size of the
scroll view. (This should not be confused with the
intrinsicContentSize method used for Auto Layout.) To size the scroll
view’s frame with Auto Layout, constraints must either be explicit
regarding the width and height of the scroll view, or the edges of the
scroll view must be tied to views outside of its subtree.
A custom view is located in IB by dragging UIView object from library and it's class name is set to the custom view class name. This custom view has a subview, which is added in init?(coder aDecoder: NSCoder).
How should anchor type constraints be composed, and where should they be located so the _centralLabel subview is set centrally in the custom view, and it's dimensions are 25% of the custom's view dimensions?
If the code is written like this:
override func updateConstraints() {
if !_subviewsConstraintsAdded {
_centralLabel.heightAnchor.constraint(equalTo: heightAnchor, multiplier: 0.25).isActive = true
_centralLabel.widthAnchor.constraint(equalTo: widthAnchor, multiplier: 0.25).isActive = true
_centralLabel.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
_centralLabel.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
_subviewsConstraintsAdded = true
}
super.updateConstraints()
}
The result of the code is, that instead of setting the size of _centralLabel to be 25% of the custom view, the custom view is shrunk to (0,0) which is the size of _centralLabel while updateConstraints() is being called.
The missing bit was that:
_centralLabel.translatesAutoresizingMaskIntoConstraints = false
which should be located somewhere after programmatically instantiating the subview with this line:
_centralLabel = UILabel()
After this correction the _centralLabel subview has enlarged and has moved to the center of the view, while the view itself remained in its original size, shape and position.
I have a Screen like this which is built in interface builder:
The control at the bottom is a UICollectionView and the other two are a UIButton and a segmentedControl. They sit on a UIView which is a child of the UIController's view but has the same frame. In my code, I add the UIViewController to a UINavigationController so there will be a UINavigationBar on the top of the screen. I can set the auto layout constraint to force the top of this screen to move below the navigationbar. However when I scroll down the UICollectionView I couldn't scroll to the bottom to view the rest of the items. I can only see half size of the last two items.
I have updated the code and put some log in viewDidAppear:
UICollectionView frame height = 527.000000
View frame = 504.000000
So the UICollectionView's height is bigger than the view's height. I want the collection view to fit right in both 3.5" screen and 4" screen.
Any idea what I have done wrong? How can I fix this?
This is because your collection view's height is exceeding that of the frame, try to reduce the height of your collection view which fits that of your UIView's height - your collection view's yOrigin.
The problem is that when the navigation bar is added, the UICollectionView gets "pushed" down but its height remains the same.
You need to add a layout constraint that will cause the UICollectionView to reduce height when adding the navigation bar. The constraint should be something along the lines of "bottom space to bottom layout guide = 0"
Swift 4.1
Try to define the constraints as follows to consider the Navigation Bar (this should work even with autorotation, when the status bar can change in height) It also considers the chante in iOS 11 for the safeArea.
let collectionView = UICollectionView()
self.view.addSubview(collectionView)
if #available(iOS 11.0, *) {
let safeArea = self.view.safeAreaLayoutGuide
collectionView.topAnchor.constraint(equalTo: safeArea.topAnchor, constant: 0).isActive = true
} else {
let topGuide = self.topLayoutGuide
collectionView.topAnchor.constraint(equalTo: topGuide.bottomAnchor, constant: 0).isActive = true
}
collectionView.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 0).isActive = true
collectionView.rightAnchor.constraint(equalTo: self.view.rightAnchor, constant: 0).isActive = true
collectionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true