I want to know what is the correct way to put a UITextView at the bottom of the TableViewController, I don't want it to put in a cell because I already know how to do that, and that is not what I am trying to do, I want to put a UITextView at the bottom of the tableViewController
class ExampleTableViewController: UITableViewController, UITextViewDelegate {
let firstView = UITextView()
first.translatesAutoresizingMaskIntoConstraints = false
func setUpTextView() {
firstView.delegate = self
view.addSubview(firstView)
NSLayoutConstraint.activate([firstView.topAnchor.constraint(equalTo: view.topAnchor),
firstView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
firstView.leftAnchor.constraint(equalTo: view.leftAnchor),
firstView.rightAnchor.constraint(equalTo: view.rightAnchor)])
} }
Replace
firstView.topAnchor.constraint(equalTo: view.topAnchor)
with
firstView.heightAnchor.constraint(equalToConstant:50)
since the view of the tablecontroller is the tableView then any view added will scroll with it so it's better to create
class ExampleTableViewController: UIViewController, UITextViewDelegate {
a usual vc with tableView && textView with constraints set properly
Edit: so either implement scrollViewDidScroll and change the bottom constraint as the table with it's tableView.contentOffset.y
OR
set the view as a footer like
self.tableView.footerView = firstView
Related
I have added a tabBarController and hooked it up to viewControllers and given the tabBarController its own class. It works, but I would like to customize it by changing the constraints so that it's not right at the bottom. From what I can see there's no way to add constraints in auto layout as it's all grayed out. I gave a shot at adding it programmatically, but nothing happens.
final class TabBarViewController: UITabBarController {
#IBOutlet var customTabBar: UITabBar!
override func viewDidLoad() {
self.selectedIndex = 2
let fontAttributes = [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 20.0)]
UITabBarItem.appearance().setTitleTextAttributes(fontAttributes, for: .normal)
//This doesn't work
customTabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Neither does this
self.tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Or this
if let tabC = self.tabBarController {
tabC.tabBar.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
}
}
So how do I do this? I want the tabBar to be about 50p from the bottom.
You can create a container view controller to hold you UITabBarController.
Then, when you set the constraints on your container view, you can add 'padding' to the bottom, to have it go upwards. Below are two screenshots explaining the layout.
I have a method to add two circles to the background view. When added to the view of a UIViewController, they get added correctly. However, when added to the view of a UITableViewController, they get added to the other side.
The method to create the circles is:
extension UIViewController {
func makeCircle(radius: CGFloat) -> UIView {
let circle = UIView()
circle.backgroundColor = .yellow
circle.translatesAutoresizingMaskIntoConstraints = false
circle.layer.cornerRadius = radius
NSLayoutConstraint.activate([
circle.widthAnchor.constraint(equalToConstant: radius * 2),
circle.heightAnchor.constraint(equalToConstant: radius * 2)])
return circle
}
}
The method to add the circles within the UIViewController extension is:
func addCircles() {
// Center right circle
let circle1Radius = CGFloat(152)
let circle1 = makeCircle(radius: circle1Radius)
view.addSubview(circle1)
NSLayoutConstraint.activate([
circle1.centerYAnchor.constraint(equalTo: view.centerYAnchor),
circle1.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: circle1Radius)])
// Bottom left circle
let circle2Radius = CGFloat(77)
let circle2 = makeCircle(radius: circle2Radius)
view.addSubview(circle2)
NSLayoutConstraint.activate([
circle2.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 70),
circle2.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -50)])
}
For both classes, the circles get added inside the viewDidLoad method:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
addCircles()
}
}
The ViewController: UIViewController, the view looks correct:
However, for the TableViewController: UITableViewController (exact same viewDidLoad as for controller above), the output looks opposite:
Can someone give me a tip on what I am doing wrong?
Thanks to #matt's comment:
I assume it's because a table view is a scroll view. Remember, constraints from a scroll view's subview to a scroll view have a completely different meaning from constraints from a normal view's subview to its superview. So adding subviews directly to a table view using constraints is going to work oddly.
What solved my issue was to no longer use a UITableViewController, but a "basic" UIViewController that has a UITableView and implements the UITableViewDataSource and the UITableViewDelegate:
class TableViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
override func viewDidLoad() {
super.viewDidLoad()
addCircles()
}
}
So I have this UITextField and I want to show a UITableView just below it. I'm trying to show the suggestions in the UITableView my logic works fine but I want to know how can I show the UITableView beneath the UITextField.
Currently, I'm doing something like this
private func setUpAutoCompleteTable() {
autocompleteTableView = UITableView(frame: CGRect(x: self.originTextField.bounds.minX,y: self.originTextField.bounds.maxY,width: self.originTextField.bounds.width,height: self.originTextField.bounds.height * 4), style: UITableView.Style.plain)
self.view.addSubview(autocompleteTableView)
autocompleteTableView.delegate = self
autocompleteTableView.dataSource = self
autocompleteTableView.isScrollEnabled = true
autocompleteTableView.isHidden = true
autocompleteTableView.register(LocationAutoCompleteCell.self, forCellReuseIdentifier: "AutoCompleteRowIdentifier")
}
But it is showing the table at the top of the screen. I want to tell you that originTextField is inside another view.
UPDATE
This is how the view hierarchy looks.
So, to layout the tableView programmatically you need to declare it in your ViewController
var autoCompleteTableView: UITableView!
Initialize it in ViewDidLoad
autoCompleteTableView = UITableView(frame: .zero) // constraints will set the frame for us later
set constraints in ViewDidLoad
NSLayoutConstraint.activate([
autoCompleteTableView.topAnchor.constraint(equalTo: textField.bottomAnchor, constant: 5), //the constant is how many pts below the textField you want the tableView to be
autoCompleteTableView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
autoCompleteTableView.widthAnchor.constraint(equalTo: view.widthAnchor),
autoCompleteTableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
and then you can set whatever tableView properties you need to. remember, your VC also needs to implement UITableViewDelegate and UITableViewDataSource
You should probably go for Gabe's answer with a detail, considering that the textfield is inside another view.
So, you'll need to get the textfield's bottom anchor reference from the Storyboard. You can do that by control dragging the constraint to your swift file.
Then you can do this:
autoCompleteTableView.topAnchor.constraint(equalTo: self.textFieldBottomAnchorReference, constant: 5)
UIViewController
ContainerView -> UITableViewController
UIView
ContainerView -> UIViewController
When the keyboard is shown, the UITableViewController in UIViewController automatically adjusts the contentInset.
So, I add this code in UITableViewController
if #available(iOS 11.0, *) {
self.tableView.contentInsetAdjustmentBehavior = .never
} else {
self.automaticallyAdjustsScrollViewInsets = false
}
But it does not work. How can I disable auto inset?
I found the perfect solution!
UITableView.appearance().contentInsetAdjustmentBehavior = .never
just add this code in UITableViewController. and strange adjust inset all gone!
Try to apply to the ViewController which contains the ContainerView (with the UITableView inside of it). It does not work when applied to the UITableView itself.
Or via code in viewDidLoad(): self.automaticallyAdjustsScrollViewInsets = false
I am trying to add a button ontop of a uitableview controller table view. The view controller has a navigation controller and static cells, which is why it is a uitableviewcontroller and not a uiviewcontroller. Now I am trying to add a button at the bottom of the screen that is attached to the navigation controller so that it doesn't scroll with the table view.
I am trying to make something similar to what is below. It has a navigation controller for the top bar, a table view with static cells and then a button, but how did they do the button?
Image: http://postimg.org/image/ilsmqqrip/
Thanks!
UPDATE: How can I use a uiviewcontroller with a tableview with static cells using Swift?
I find Container Views very useful in this scenario! A clean solution and very easy to implement.
Just create a normal UIViewController, add your button and a ContainerView as subviews of this UIViewController (the middle one in the image below). Finally create Embed Segue from ContainerView to your UITableViewController (the one on the right).
This way you can use static cell prototypes, not being limited only to UITableView at the same time.
Result:
there is a better solution for this. you can do this by disabling the Auto Layout(button.translatesAutoresizingMaskIntoConstraints = false) property of the corresponding Button or any UIView for floating button:
Swift 4
//create a button or any UIView and add to subview
let button=UIButton.init(type: .system)
button.setTitle("NEXT", for: .normal)
button.frame.size = CGSize(width: 100, height: 50)
self.view.addSubview(button)
//set constrains
button.translatesAutoresizingMaskIntoConstraints = false
if #available(iOS 11.0, *) {
button.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor, constant: -10).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
} else {
button.rightAnchor.constraint(equalTo: tableView.layoutMarginsGuide.rightAnchor, constant: 0).isActive = true
button.bottomAnchor.constraint(equalTo: tableView.layoutMarginsGuide.bottomAnchor, constant: -10).isActive = true
}
I did something similar with UITableViewController and a static datasource. I added the button in the footerview of my tableview.
To make it align to the bottom of the screen i needed this code in my viewcontroller:
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
}
In short, I resize the footerview to take up all the remaining space after the contentsize of the table view is removed. Since the button is aligned to the bottom of the footerView with autolayout, it will stay in the bottom of the screen.
The Storyboard:
Here is the result:
The UITableViewController will take up the whole space, so you won't be able to add the button. Refactor your UITableViewController based code into UIViewController with UITableView manually added. This way you will be able to set the size of your table view and put the button to the bottom.
Unfortunately UITableViewController has a tableView as its top level view. Of course if you look in the view debugger you can see that the tableview is not the root view. Therefore you can add the buttons to the tableView's window programatically. If you have to do it after the fact, this is probably the easiest way to add a top level element over a UITableViewController. Otherwise if you are doing it in the initial design, you can use container view for your buttons and a UITableViewController for the TableView. The downside of this approach is you end up with two view controllers, one for the container and one for the table and its often necessary to pass information back and for between them. If you are using swift you can simplify this by nesting the tableViewcontroller inside the container view controller class.
If you want to add a button to the window, you can do this lazily once you are sure that the view has a window. Note that the buttons belong to the window and not to the view controller, so its your responsibility to remove them when the view controller disappears.
private weak var button: UIButton!
...
override func didMove(toParentViewController parent: UIViewController?) {
super.didMove(toParentViewController: parent)
guard self.button == nil, let window = tableView.window else {
return
}
let button = UIButton(frame: CGRect(x:0, y:40, width: 200, height: 20))
button.setTitle("This is a red button", for: .normal)
button.backgroundColor = UIColor.red
window.addSubview(button)
self.button = button
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
button?.removeFromSuperview()
}
Step 1 :-
Drag and drop one uiview to UITable View Controller (Static)
Automatically it sticks to the bottom.
If you need to, you can also add two buttons inside UIView... It depends on your requirements.
Step 2 :-
Connect the outlet for uiview (outletView)
Step 3 :-
Add this below code in View Will Appear.
override func viewWillAppear(_ animated: Bool) {
outletViewBottom.backgroundColor = .red
tableView.addSubview(outletViewBottom)
// set position
outletView.translatesAutoresizingMaskIntoConstraints = false
outletView.leftAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.leftAnchor).isActive = true
outletView.rightAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.rightAnchor).isActive = true
outletView.bottomAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.bottomAnchor).isActive = true
outletView.widthAnchor.constraint(equalTo: tableView.safeAreaLayoutGuide.widthAnchor).isActive = true
outletView.heightAnchor.constraint(equalToConstant: 50).isActive = true // specify the height of the view
}
Step 4 :-
Now run the code... Happy coding.
all you need to do is to add your Top view whichever it is to the navigationController.view like so:
self.navigationController?.view.addSubview(YOUR_TOP_VIEW)
so if you need a sticky button/view etc... on top of TableViewController which does not scroll with tableView, use this approach.
Here is a UIViewController, with a UITableView added as a subview. At the top right, you can see a dropdown that says Content: Dynamic Prototypes. Change it to Static Cells.