uiview over uinavigationcontroller - ios

I have a problem when adding uiview under navigation controller. why my uiview is on top of uinavigationcontroller, I want to add my uiview under navigationController. this is my code.
let slideView = UIView()
view.backgroundColor = .white
navigationItem.title = "Absensi"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.barTintColor = .white
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "NunitoSans-SemiBold", size: 20)]
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic-back-line").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleBack))
view.addSubview(slideView)
slideView.backgroundColor = .red
slideView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
slideView.topAnchor.constraint(equalTo: view.topAnchor),
slideView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
slideView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
slideView.heightAnchor.constraint(equalToConstant: 80)
])

Set your constraints with respect to safe area.

You should be add the topAnchor to safe area -> view.safeAreaLayoutGuide.topAnchor
NSLayoutConstraint.activate([
slideView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
slideView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
slideView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
slideView.heightAnchor.constraint(equalToConstant: 80)
])

just try using frame instead of constraint..this code should work
override func viewDidAppear(_ animated: Bool) {
let slideView = UIView(frame: CGRect(x: 0, y:
navigationController?.navigationBar.frame.height ?? 0 + 20 ,width: UIScreen.main.bounds.width, height: 80))
slideView.backgroundColor = .red
slideView.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
navigationItem.title = "Absensi"
navigationController?.navigationBar.prefersLargeTitles = true
navigationController?.navigationBar.barTintColor = .white
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.largeTitleTextAttributes = [NSAttributedString.Key.font: UIFont(name: "NunitoSans-SemiBold", size: 20)]
navigationItem.leftBarButtonItem = UIBarButtonItem(image: #imageLiteral(resourceName: "ic-back-line").withRenderingMode(.alwaysOriginal), style: .plain, target: self, action: #selector(handleBack))
view.addSubview(slideView)
}

Related

Proper positioning of UIToolBar at the bottom of the screen

Due to lack of practice with creating UIToolBar element, I don't fully understand what is the cleanest way to position UIToolBar at the bottom of the screen. Now my code looks like this:
private lazy var toolBar: UIToolbar = {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height))
toolBar.translatesAutoresizingMaskIntoConstraints = false
let addNewNote = UIBarButtonItem(barButtonSystemItem: .compose, target: toolBar, action: nil)
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: toolBar, action: nil)
toolBar.items = [spacer, addNewNote]
return toolBar
}()
private func setUpConstraints() {
NSLayoutConstraint.activate([
toolBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
toolBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
toolBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
}
This gives desired results, however I feel like I am missing a better way to implement this simple element. First of all, once the device is rotated, ToolBar doesn't fill all the space from leading to trailing, secondly assigning both leading/trailing constraints and width/height based on UIScreen.bounds property just doesn't seem right, although previously I saw a constraints layout issue of ToolBarElement in the console before I assigned this strange frame to my ToolBar. Now this issue is gone, but the situation did not become clearer.
My goal is to clone UIToolBar from iOS native notes app
So, as an experiences iOS developer (of course you are!) what would you recommend me to do?
Have a nice day!
If I understand well you can do it with autolayaout:
Declare your objects under your controller class:
let toolBar: UIToolbar = {
//let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)) // remove this
let toolBar = UIToolbar()
toolBar.translatesAutoresizingMaskIntoConstraints = false
let addNewNote = UIBarButtonItem(barButtonSystemItem: .compose, target: toolBar, action: nil)
let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: toolBar, action: nil)
toolBar.tintColor = .yourColor
let toolBarTitleLabel = UILabel()
toolBarTitleLabel.text = "150 Results"
toolBarTitleLabel.sizeToFit()
toolBarTitleLabel.backgroundColor = .clear
toolBarTitleLabel.textColor = .white
toolBarTitleLabel.textAlignment = .center
let topBarButtonItemTitleLabel = UIBarButtonItem(customView: toolBarTitleLabel)
toolBar.items = [spacer, topBarButtonItemTitleLabel, spacer, addNewNote]
return toolBar
}()
let mylabel2: UILabel = {
let label = UILabel()
label.text = "Your results data text here..."
label.textAlignment = .center
label.textColor = .gray
label.font = .systemFont(ofSize: 16, weight: .regular)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
let dummyView: UIView = {
let v = UIView()
v.backgroundColor = .systemBlue
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
In viewDidLoad set constraints:
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.addSubview(toolBar)
NSLayoutConstraint.activate([
toolBar.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
toolBar.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
toolBar.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor)
])
view.addSubview(mylabel2)
NSLayoutConstraint.activate([
mylabel2.bottomAnchor.constraint(equalTo: toolBar.topAnchor),
mylabel2.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 30),
mylabel2.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -30),
mylabel2.heightAnchor.constraint(equalToConstant: 50)
])
view.addSubview(dummyView)
NSLayoutConstraint.activate([
dummyView.bottomAnchor.constraint(equalTo: mylabel2.topAnchor),
dummyView.leadingAnchor.constraint(equalTo: mylabel2.leadingAnchor),
dummyView.trailingAnchor.constraint(equalTo: mylabel2.trailingAnchor),
dummyView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 30)
])
}
This is the result:

Set Constraints to Labels So That One is At Leading Side and The Other at Trailing Side in Swift

I am programmatically adding a label and a button in Swift. I want the label to be at leading side and the button at trailing side. This is what I did so far:
let view = UIView.init(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
view.clipsToBounds = true
view.translatesAutoresizingMaskIntoConstraints = false
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
button.frame = CGRect.init(x: 100, y: 0, width: view.frame.width-25, height: view.frame.height-25)
label.frame = CGRect.init(x: 10 , y: 0, width: view.frame.width-10, height: view.frame.height-10)
view.addSubview(label)
view.addSubview(button)
And this results in:
My desired result is:
I want see all to be at trailing side and the top-rated label at the leading side. How can I achieve it in Swift?
Question is a little unclear, I think you are asking how to achieve the same layout with constraints instead of using frames?
Here is a rough sample of how to do it, you might need to tweak the constants:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
let view = UIView(frame: CGRect.init(x: 0, y: 0, width: view.frame.width, height: 50))
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 18, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
view.addSubview(label)
view.addSubview(button)
NSLayoutConstraint.activate([
label.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16),
label.trailingAnchor.constraint(equalTo: button.leadingAnchor, constant: 16),
label.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
label.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16),
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 16),
button.topAnchor.constraint(equalTo: view.topAnchor, constant: 16),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 16)
])
self.view.addSubview(view)
}
override func viewDidLoad() {
super.viewDidLoad()
let label = UILabel()
label.text = "Top-rated experts near you"
label.font = UIFont.systemFont(ofSize: 21, weight: .medium)
label.textColor = .black
label.contentMode = .left
let button = UIButton()
button.backgroundColor = .clear
button.setTitle("See All", for: .normal)
button.setTitleColor(.black, for: .normal)
button.contentMode = .right
button.addTarget(self, action: #selector(buttonTapped(sender:)), for: .touchUpInside)
let stack = UIStackView(arrangedSubviews: [label, button])
stack.axis = .horizontal
stack.distribution = .fill
stack.alignment = .center
stack.spacing = 16
stack.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stack)
let padding: CGFloat = 16
let viewHeight: CGFloat = 50
NSLayoutConstraint.activate([
stack.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: padding),
stack.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: padding),
stack.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: 1-padding),
stack.heightAnchor.constraint(equalToConstant: viewHeight)
])
}

Swift: Programmatic Autolayout broken by UNavigationController

I have inherited a UIKit app where my predecessor has written all the interface code by hand. I have a view controller, a simple login screen that works just fine, but when I add it to a UINavigationController it is oddly stretched. This doesn't make a lot of sense to me.
I feel like I must be missing some simple flag, or what do I need to do to make the Navcontroller play nice with this programmatic autolayout (which is hopefully my last ever)
// this a view controller extension
func apply(constraints: [NSLayoutConstraint]) {
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate(constraints)
}
There's my handy dandy, seemingly sane utility for applying constraints. As an example, the nice little oval thing making my password textfield looks nice has these constraints set up.
// these constraints are being setup in the view controller
emailCapsuleView.apply(constraints: [
emailCapsuleView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24.0),
emailCapsuleView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
emailCapsuleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8),
emailCapsuleView.heightAnchor.constraint(equalToConstant: Dimensions.inputFieldHeight)
])
Outside of a UINavigationController everything is fine,
but in a UINavigationController everythhing is super wide and broken (simulator shot)
The width is not the problem here - if I use a constant for the width, my subview is still off center despite having it's centerX sets to the view controller's view's center X.
Configure your navigation controller and after that try like this:
set your objects:
let emailTextfield: UITextField = {
let tf = UITextField()
tf.backgroundColor = .white
tf.layer.borderWidth = 1
tf.layer.borderColor = UIColor.lightGray.cgColor
tf.setPadding(left: 10, right: 10)// use my extension below
tf.attributedPlaceholder = NSAttributedString(string: "Email address", attributes: [.foregroundColor: UIColor.lightGray])
tf.layer.cornerRadius = 14
tf.clipsToBounds = true
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let passTextfield: UITextField = {
let tf = UITextField()
tf.backgroundColor = .white
tf.layer.borderWidth = 1
tf.layer.borderColor = UIColor.lightGray.cgColor
tf.setPadding(left: 10, right: 10)// use my extension below
tf.attributedPlaceholder = NSAttributedString(string: "Password", attributes: [.foregroundColor: UIColor.lightGray])
tf.layer.cornerRadius = 14
tf.clipsToBounds = true
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let loginButton: UIButton = {
let b = UIButton(type: .system)
b.backgroundColor = .black
b.setTitle("Save Image", for: .normal)
b.titleLabel?.font = .systemFont(ofSize: 16, weight: .semibold)
b.setTitleColor(.white, for: .normal)
b.layer.cornerRadius = 14
b.clipsToBounds = true
b.translatesAutoresizingMaskIntoConstraints = false
return b
}()
let subBigTitle: UILabel = {
let l = UILabel()
l.text = "Your Big Title"
l.font = .systemFont(ofSize: 30, weight: .regular)
l.textColor = .black
l.textAlignment = .center
l.translatesAutoresizingMaskIntoConstraints = false
return l
}()
Now in viewDidLoad set stackView, bigTitle label and constraints:
view.addSubview(subBigTitle)
subBigTitle.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
subBigTitle.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
subBigTitle.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
subBigTitle.heightAnchor.constraint(equalToConstant: 60).isActive = true
let stackView = UIStackView(arrangedSubviews: [emailTextfield, passTextfield, loginButton])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(stackView)
stackView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.8).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 170).isActive = true
stackView.topAnchor.constraint(equalTo: subBigTitle.bottomAnchor, constant: 24).isActive = true
stackView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
to add text padding to your textfield use my extension:
extension UITextField {
func setPadding(left: CGFloat, right: CGFloat){
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: left, height: self.frame.size.height))
self.leftView = paddingView
self.leftViewMode = .always
let paddingViewRight = UIView(frame: CGRect(x: 0, y: 0, width: right, height: self.frame.size.height))
self.rightView = paddingViewRight
self.rightViewMode = .always
}
}
and this is the result:

Subview not getting displayed

I am trying to add a subview using layout constraint. The subview is not getting displayed. Its displaying only when the frame is specified. However I want to achieve the it using layout constraints
import UIKit
class PhotoViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .lightGray
configurePhotoBarButton()
buildSubView()
}
#objc func closeClicked() {
dismiss(animated: true, completion: nil)
}
func configurePhotoBarButton() {
navigationController?.navigationBar.barStyle = .black
navigationController?.navigationBar.barTintColor = .lightGray
navigationItem.title = "Take a Photo"
let titleAttributes = [NSAttributedString.Key.font:UIFont(name: "DIN Condensed", size: 30),NSAttributedString.Key.foregroundColor: UIColor.black]
self.navigationController?.navigationBar.titleTextAttributes = titleAttributes
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Close", style: .plain, target: self, action: #selector(closeClicked))
let attributes = [NSAttributedString.Key.font:UIFont(name: "DIN Condensed", size: 20),NSAttributedString.Key.foregroundColor: UIColor.black]
navigationItem.leftBarButtonItem?.setTitleTextAttributes(attributes, for: .normal)
}
func buildSubView() {
//Add a Image View
let testview = UIView()
testview.backgroundColor = .orange
self.view.addSubview(testview)
testview.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
testview.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
testview.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
testview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
}
}
You are missing set translatesAutoresizingMaskIntoConstraints.
Please set it equal false when you want to add constraint programmatically.
testview.translatesAutoresizingMaskIntoConstraints = false

Constraints for two UIStackView - one under the other

I working on "Hangman" app. On the top I want image, in middle textfield, and bottom couple UIStackView row for letters. Now I try to add constraints for only two UIStackView one under the other, but one is always behind other ie. not see.
This is image where you can se only second stack view (named: stacklView2), and the first i cant see. Why? What I doing wrong?
And this is code:
import UIKit
class ViewController: UIViewController {
var imageView: UIImageView!
var answerTextfield: UITextField!
override func loadView() {
view = UIView()
view.backgroundColor = .white
imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.layer.borderWidth = 1
view.addSubview(imageView)
answerTextfield = UITextField()
answerTextfield.translatesAutoresizingMaskIntoConstraints = false
answerTextfield.textAlignment = .center
answerTextfield.isUserInteractionEnabled = false
answerTextfield.layer.borderWidth = 1
view.addSubview(answerTextfield)
let button1 = UIButton()
button1.backgroundColor = .red
let button2 = UIButton()
button2.backgroundColor = .blue
let stackView1 = UIStackView(arrangedSubviews: [button1, button2])
stackView1.translatesAutoresizingMaskIntoConstraints = false
stackView1.distribution = .fillEqually
stackView1.axis = .horizontal
view.addSubview(stackView1)
let stackView2 = UIStackView(arrangedSubviews: [button2, button1])
stackView2.translatesAutoresizingMaskIntoConstraints = false
stackView2.distribution = .fillEqually
stackView2.axis = .horizontal
view.addSubview(stackView2)
NSLayoutConstraint.activate([
imageView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 5),
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
answerTextfield.topAnchor.constraint(equalTo: imageView.bottomAnchor, constant: 20),
answerTextfield.widthAnchor.constraint(equalTo: view.layoutMarginsGuide.widthAnchor, multiplier: 0.5),
answerTextfield.centerXAnchor.constraint(equalTo: view.centerXAnchor),
stackView1.topAnchor.constraint(equalTo: answerTextfield.bottomAnchor, constant: 20),
stackView1.widthAnchor.constraint(equalTo: view.layoutMarginsGuide.widthAnchor, multiplier: 1.0),
stackView1.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor),
//stackView1.bottomAnchor.constraint(equalTo: stackView2.layoutMarginsGuide.topAnchor, constant: -5),
stackView2.topAnchor.constraint(equalTo: stackView1.bottomAnchor, constant: 20),
stackView2.widthAnchor.constraint(equalTo: view.layoutMarginsGuide.widthAnchor, multiplier: 1.0),
stackView2.centerXAnchor.constraint(equalTo: view.layoutMarginsGuide.centerXAnchor),
stackView2.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -5),
])
}
override func viewDidLoad() {
super.viewDidLoad()
imageView.image = UIImage(named: "Hangman-0")
answerTextfield.text = "T E X T F I E L D"
setupNavigationBar()
}
func setupNavigationBar() {
let hintButton = UIButton(type: .infoLight)
hintButton.addTarget(self, action: #selector(hintTapped), for: .touchUpInside)
navigationItem.leftBarButtonItem = UIBarButtonItem(customView: hintButton)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(newGameTapped))
}
#objc func hintTapped() {
print("Hint button tapped!")
}
#objc func newGameTapped() {
print("New Game button tapped!")
}
}
You use same objects button1 and button2 in both stacks
let stackView1 = UIStackView(arrangedSubviews: [button1, button2])
let stackView2 = UIStackView(arrangedSubviews: [button2, button1])
so it's added to stackView2 only you need to create 2 other different ones as a single element can be added to only 1 view

Resources