Adding two buttons to UIStackView horizontally one covers the other - ios

So I'm trying to add two buttons of equal width to my view next to each other. I have used a stack view as I thought this would work better.
The second button named "Sort" is only one visible at the correct width but covers over my "Add" button leaving and empty space next to it.
I have run it without the "Sort" button and I just get the "Add" button showing across the whole stack view as I would expect so bit confused but this.
Code is below not sure what's going on with it. If think its better to not use stackView need just a little bit of advice on best way to work the constraints. Thanks everyone.
And just to say I'm working only in programmatic with this.
import UIKit
class PlacesVC: UIViewController {
let topTitle = UILabel()
let logoImage = UIImage(named: "DIYCoffeeLogoDark")
let logoImageView = UIImageView()
let addButton = UIButton()
let sortButton = UIButton()
let buttonStack = UIStackView()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = #colorLiteral(red: 0.9324248433, green: 0.9268818498, blue: 0.9366856217, alpha: 1)
logoImageView.image = logoImage
self.navigationItem.titleView = logoImageView
self.navigationController?.navigationBar.isHidden = false
view.addSubview(topTitle)
view.addSubview(buttonStack)
setUpTopTital()
setUpButtonStack()
}
func setUpTopTital() {
topTitle.translatesAutoresizingMaskIntoConstraints = false
topTitle.font = UIFont(name: "Futura", size: 25)
topTitle.text = "Your Saved Places"
topTitle.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
topTitle.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 20).isActive = true
topTitle.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -20).isActive = true
topTitle.heightAnchor.constraint(equalToConstant: 30).isActive = true
}
func setUpButtonStack() {
buttonStack.translatesAutoresizingMaskIntoConstraints = false
buttonStack.addArrangedSubview(addButton)
buttonStack.addArrangedSubview(sortButton)
buttonStack.distribution = .fillProportionally
buttonStack.alignment = .center
buttonStack.axis = .horizontal
buttonStack.spacing = 20
buttonStack.topAnchor.constraint(equalTo: topTitle.bottomAnchor, constant: 20).isActive = true
buttonStack.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
buttonStack.widthAnchor.constraint(equalToConstant: view.frame.width - 40).isActive = true
buttonStack.heightAnchor.constraint(equalToConstant: 30).isActive = true
setUpAddButton()
setUpSortButton()
}
func setUpAddButton() {
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.titleLabel?.font = UIFont(name: "Futura", size: 20)
addButton.setTitle("ADD", for: .normal)
addButton.backgroundColor = #colorLiteral(red: 0.3176470697, green: 0.07450980693, blue: 0.02745098062, alpha: 1)
addButton.layer.cornerRadius = 5
addButton.setTitleColor(.white, for: .normal)
}
func setUpSortButton() {
addButton.translatesAutoresizingMaskIntoConstraints = false
addButton.titleLabel?.font = UIFont(name: "Futura", size: 20)
addButton.setTitle("SORT", for: .normal)
addButton.backgroundColor = #colorLiteral(red: 0.3176470697, green: 0.07450980693, blue: 0.02745098062, alpha: 1)
addButton.layer.cornerRadius = 5
addButton.setTitleColor(.white, for: .normal)
}

In both functions, setUpAddButton() and setUpSortButton() you are using same button that is addButton.
Change addButton to sortButton inside setUpSortButton(), then you are able to see both buttons in your stackview.
func setUpSortButton() {
sortButton.translatesAutoresizingMaskIntoConstraints = false
sortButton.titleLabel?.font = UIFont(name: "Futura", size: 20)
sortButton.setTitle("SORT", for: .normal)
sortButton.backgroundColor = #colorLiteral(red: 0.3176470697, green: 0.07450980693, blue: 0.02745098062, alpha: 1)
sortButton.layer.cornerRadius = 5
sortButton.setTitleColor(.white, for: .normal)
}

Related

Back button doesn't work when changing background color

I have an app which displays a list of fruits. When you click on one of them, a view pops up with all the information and nutritional values within that fruit. If the fruit I click on contains more than 10 in sugar, the background should repeat between red and white. My problem is that I have a back button (Tilbake) which works on every view except for the ones that have the animated background color with over 10 in sugar. The ability to swipe the view down stops working too. It seems to be some kind of layer which is on top of the screen stopping me from actually touching the screen. How do I solve this ?
import UIKit
class FruitDetailVC: UIViewController{
var fruit: Fruit?
var safeArea: UILayoutGuide!
let nameLabel = UILabel()
let genusLabel = UILabel()
let familyLabel = UILabel()
let orderLabel = UILabel()
let idLabel = UILabel()
let carbLabel = UILabel()
let proteinLabel = UILabel()
let fatLabel = UILabel()
let caloriesLabel = UILabel()
let sugarLabel = UILabel()
let dividerLabel = UILabel()
let sugarWarningLabel = UILabel()
let dismissButton = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
setupdata()
setupLabel()
setupDismissButton()
safeArea = view.layoutMarginsGuide
view.backgroundColor = .white
if (fruit?.nutritions.sugar)! > 10{
sugarWarningLabel.text = "This contains over 10 of sugar!!!"
animatedBackground()
}
}
func animatedBackground(){
UIView.animate(withDuration: 1.0, delay: 0.0, options:[.repeat, .autoreverse], animations: {
self.view.backgroundColor = UIColor(red: 255/255, green: 0/255, blue: 0/255, alpha: 1.0)
}, completion:{
(completed : Bool) -> Void in
UIView.animate(withDuration: 1.0, delay: 0.0, options:[.repeat, .autoreverse], animations: {
self.view.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1.0)
}, completion:nil)
})
}
func setupLabel(){
view.addSubview(nameLabel)
view.addSubview(genusLabel)
view.addSubview(familyLabel)
view.addSubview(orderLabel)
view.addSubview(idLabel)
view.addSubview(carbLabel)
view.addSubview(proteinLabel)
view.addSubview(fatLabel)
view.addSubview(caloriesLabel)
view.addSubview(sugarLabel)
view.addSubview(dividerLabel)
view.addSubview(sugarWarningLabel)
//General infromation about selected fruit
nameLabel.translatesAutoresizingMaskIntoConstraints = false
nameLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
nameLabel.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
nameLabel.font = UIFont(name: "Verdana-Bold", size: 16)
genusLabel.translatesAutoresizingMaskIntoConstraints = false
genusLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
genusLabel.topAnchor.constraint(equalTo: nameLabel.bottomAnchor).isActive = true
genusLabel.font = UIFont(name: "Verdana", size: 14)
familyLabel.translatesAutoresizingMaskIntoConstraints = false
familyLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
familyLabel.topAnchor.constraint(equalTo: genusLabel.bottomAnchor).isActive = true
familyLabel.font = UIFont(name: "Verdana", size: 14)
orderLabel.translatesAutoresizingMaskIntoConstraints = false
orderLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
orderLabel.topAnchor.constraint(equalTo: familyLabel.bottomAnchor).isActive = true
orderLabel.font = UIFont(name: "Verdana", size: 14)
idLabel.translatesAutoresizingMaskIntoConstraints = false
idLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
idLabel.topAnchor.constraint(equalTo: orderLabel.bottomAnchor).isActive = true
idLabel.font = UIFont(name: "Verdana", size: 14)
//Nutrition information divider
dividerLabel.translatesAutoresizingMaskIntoConstraints = false
dividerLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dividerLabel.topAnchor.constraint(equalTo: idLabel.bottomAnchor).isActive = true
dividerLabel.font = UIFont(name: "Verdana-Bold", size: 16)
//Nutrition information
carbLabel.translatesAutoresizingMaskIntoConstraints = false
carbLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
carbLabel.topAnchor.constraint(equalTo: dividerLabel.bottomAnchor).isActive = true
carbLabel.font = UIFont(name: "Verdana", size: 14)
proteinLabel.translatesAutoresizingMaskIntoConstraints = false
proteinLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
proteinLabel.topAnchor.constraint(equalTo: carbLabel.bottomAnchor).isActive = true
proteinLabel.font = UIFont(name: "Verdana", size: 14)
fatLabel.translatesAutoresizingMaskIntoConstraints = false
fatLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
fatLabel.topAnchor.constraint(equalTo: proteinLabel.bottomAnchor).isActive = true
fatLabel.font = UIFont(name: "Verdana", size: 14)
sugarLabel.translatesAutoresizingMaskIntoConstraints = false
sugarLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
sugarLabel.topAnchor.constraint(equalTo: fatLabel.bottomAnchor).isActive = true
sugarLabel.font = UIFont(name: "Verdana", size: 14)
sugarWarningLabel.translatesAutoresizingMaskIntoConstraints = false
sugarWarningLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
sugarWarningLabel.topAnchor.constraint(equalTo: sugarLabel.bottomAnchor).isActive = true
sugarWarningLabel.font = UIFont(name: "Verdana-Bold", size: 14)
}
func setupdata(){
if let fruit = fruit{
nameLabel.text = fruit.name
genusLabel.text = "Genus : \(fruit.genus)"
familyLabel.text = "Family : \(fruit.family)"
orderLabel.text = "Order : \(fruit.order)"
dividerLabel.text = "Nutritional value"
let id = fruit.id
let idString = String(id)
idLabel.text = "ID : \(idString)"
let carbs = fruit.nutritions.carbohydrates
let carbString = String(carbs)
carbLabel.text = "Carbohydrates : \(carbString)"
let protein = fruit.nutritions.protein
let proteinString = String(protein)
proteinLabel.text = "Proteins : \(proteinString)"
let fat = fruit.nutritions.fat
let fatString = String(fat)
fatLabel.text = "Fat : \(fatString)"
let calories = fruit.nutritions.calories
let calorieString = String(calories)
caloriesLabel.text = "Carbohydrates : \(calorieString)"
let sugar = fruit.nutritions.sugar
let sugarString = String(sugar)
sugarLabel.text = "Sugar : \(sugarString)"
}
}
func setupDismissButton(){
view.addSubview(dismissButton)
dismissButton.translatesAutoresizingMaskIntoConstraints = false
dismissButton.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
dismissButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
dismissButton.setTitle("Tilbake",for: .normal)
dismissButton.setTitleColor(UIColor.black, for: .normal)
dismissButton.addTarget(self, action: #selector(dismissAction), for: .touchUpInside)
}
#objc func dismissAction(){
self.dismiss(animated: true)
}
}
The "presentation layer" of your animation is probably covering your dismiss button and preventing it from being tapped. Try adding a print statement to your dismissAction() and see if it gets called when you tap the button on a screen with the animating background.
That's a guess, but a pretty good guess.
To fix the problem, try adding a view to your view's content view, under all the other views (button, labels, etc.) Let's call that new view your colorView. Change your animation to animate the background color of the colorView instead of animating the color of the view controller's content view. I'm not certain that will fix it, but it's worth a try.

Programmatic layouts showing error in landscape mode

Here is the screenshot if the errorI am building the ui of the app programatically and when i run the app it runs perfectly but when i turn the simulation in landscape mode the console shows some layouts errors. Is it okay to just tick the orientation to portrait only in the general app settings because my app is running perfectly in portrait mode and i don't want to run the app in any other orientations or am i making some problems in constraints? Here is my code for the ui...
`let top = UIColor(red: 217/255, green: 30/255, blue: 133/255, alpha: 1)
let bottom = UIColor(red: 242/255, green: 56/255, blue: 15/255, alpha: 1)
let colorOne = UIColor(red: 38/255, green: 38/255, blue: 38/255, alpha: 1)
let colorTwo = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1)
let colorTop = UIColor.red
let colorBottom = UIColor.green
let topContainer: UIView = {
let top = UIView()
top.translatesAutoresizingMaskIntoConstraints = false
top.backgroundColor = .black
return top
}()
let model: UIImageView = {
let mymodel = UIImageView(image: #imageLiteral(resourceName: "bodybuilder"))
mymodel.translatesAutoresizingMaskIntoConstraints = false
mymodel.contentMode = .scaleToFill
return mymodel
}()
let logo: UIImageView = {
let gymble = UIImageView(image: #imageLiteral(resourceName: "Gymble"))
gymble.contentMode = .scaleAspectFill
gymble.translatesAutoresizingMaskIntoConstraints = false
return gymble
}()
let bottomContainer: UIView = {
let bottom = UIView()
bottom.translatesAutoresizingMaskIntoConstraints = false
return bottom
}()
let pinField: UITextField = {
let phone = UITextField()
phone.backgroundColor = .white
phone.text = "+91"
phone.textAlignment = .center
phone.layer.cornerRadius = 5
phone.font = UIFont(name: "Roboto-Regular", size: 20)
phone.keyboardType = UIKeyboardType.numberPad
phone.translatesAutoresizingMaskIntoConstraints = false
return phone
}()
let phoneFeild: UITextField = {
let phone = UITextField()
phone.backgroundColor = .white
phone.placeholder = "Phone number"
phone.layer.cornerRadius = 5
phone.font = UIFont(name: "Roboto-Regular", size: 20)
phone.keyboardType = UIKeyboardType.phonePad
phone.keyboardAppearance = UIKeyboardAppearance.dark
phone.translatesAutoresizingMaskIntoConstraints = false
return phone
}()
let loginButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Login", for: .normal)
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 5
button.layer.masksToBounds = true
button.titleLabel?.font = UIFont(name: "Roboto-Medium", size: 22)
button.addTarget(self, action: #selector(someButtonAction), for: .touchUpInside)
return button
}()
let laterButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("I'll login later", for: .normal)
button.setTitleColor(.gray, for: .normal)
button.titleLabel?.font = UIFont(name: "Roboto-Light", size: 16)
return button
}()
let horizontalStack: UIStackView = {
let mystack = UIStackView()
mystack.alignment = UIStackView.Alignment.center
mystack.axis = NSLayoutConstraint.Axis.horizontal
mystack.translatesAutoresizingMaskIntoConstraints = false
mystack.distribution = .fill
mystack.spacing = 15
return mystack
}()
let stack: UIStackView = {
let mystack = UIStackView()
mystack.alignment = UIStackView.Alignment.center
mystack.axis = NSLayoutConstraint.Axis.vertical
mystack.translatesAutoresizingMaskIntoConstraints = false
mystack.distribution = .equalSpacing
return mystack
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .black
view.addSubview(topContainer)
topContainerLayout()
topContainer.addSubview(model)
modelLayout()
view.addSubview(bottomContainer)
bottomContainerLayout()
bottomContainer.addSubview(stack)
stackLayout()
stack.addArrangedSubview(logo)
logoLayout()
stack.addArrangedSubview(horizontalStack)
horizontalStackLayout()
horizontalStack.addArrangedSubview(pinField)
pinFieldLayout()
let padding = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: self.phoneFeild.frame.height))
phoneFeild.leftView = padding
phoneFeild.leftViewMode = UITextField.ViewMode.always
horizontalStack.addArrangedSubview(phoneFeild)
phoneFieldLayout()
let gradientWidth = (UIScreen.main.bounds.width - 32)
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [top.cgColor, bottom.cgColor]
gradientLayer.locations = [0.15, 1]
gradientLayer.startPoint = CGPoint(x: 0, y: 0)
gradientLayer.endPoint = CGPoint(x: 1, y: 0)
gradientLayer.frame = CGRect(x: 0, y: 0, width: gradientWidth, height: 50)
gradientLayer.cornerRadius = 5
loginButton.layer.insertSublayer(gradientLayer, at: 0)
stack.addArrangedSubview(loginButton)
loginButtonLayout()
stack.addArrangedSubview(laterButton)
LaterButtonLayout()
}
func LaterButtonLayout(){
laterButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
}
func loginButtonLayout(){
loginButton.widthAnchor.constraint(equalTo: stack.widthAnchor, constant: -32).isActive = true
loginButton.heightAnchor.constraint(equalToConstant: 44).isActive = true
}
func logoLayout(){
logo.topAnchor.constraint(equalTo: stack.topAnchor).isActive = true
logo.heightAnchor.constraint(equalToConstant: 56).isActive = true
logo.widthAnchor.constraint(equalToConstant: 60).isActive = true
}
func stackLayout(){
stack.heightAnchor.constraint(equalTo: bottomContainer.heightAnchor).isActive = true
stack.widthAnchor.constraint(equalTo: bottomContainer.widthAnchor).isActive = true
}
func horizontalStackLayout(){
horizontalStack.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -16).isActive = true
horizontalStack.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 16).isActive = true
horizontalStack.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
func pinFieldLayout(){
pinField.leadingAnchor.constraint(equalTo: horizontalStack.leadingAnchor).isActive = true
pinField.topAnchor.constraint(equalTo: horizontalStack.topAnchor).isActive = true
pinField.bottomAnchor.constraint(equalTo: horizontalStack.bottomAnchor).isActive = true
pinField.widthAnchor.constraint(equalToConstant: 50).isActive = true
}
func phoneFieldLayout(){
phoneFeild.leadingAnchor.constraint(equalTo: pinField.trailingAnchor, constant: 15).isActive = true
phoneFeild.topAnchor.constraint(equalTo: horizontalStack.topAnchor).isActive = true
phoneFeild.bottomAnchor.constraint(equalTo: horizontalStack.bottomAnchor).isActive = true
}
func modelLayout(){
model.topAnchor.constraint(equalTo: topContainer.topAnchor).isActive = true
model.leadingAnchor.constraint(equalTo: topContainer.leadingAnchor).isActive = true
model.trailingAnchor.constraint(equalTo: topContainer.trailingAnchor).isActive = true
model.heightAnchor.constraint(equalTo: topContainer.heightAnchor).isActive = true
}
func bottomContainerLayout(){
bottomContainer.topAnchor.constraint(equalTo: topContainer.bottomAnchor).isActive = true
bottomContainer.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
bottomContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
bottomContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
func topContainerLayout(){
topContainer.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
topContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 2/3 , constant: -20).isActive = true
topContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
topContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}
#objc func someButtonAction(){
print("Button is tapped!")
let opt = OTPViewController()
present(opt, animated: true, completion: nil)
}
}
`
If you want to customize app orientation do this:
Go to General setting of project
in Deployment , change device orientation .
Probably you didn't make auto layout.
Please check this out -> Auto Layout Guide
And this -> Programmatically Creating Constraints
Edit:
Use that translatesAutoresizingMaskIntoConstraints = false after addSubView.For example
func topContainerLayout(){
topContainer.translatesAutoresizingMaskIntoConstraints = false
topContainer.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
topContainer.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 2/3 , constant: -20).isActive = true
topContainer.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
topContainer.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
}

How to combine repetitive code that just styles segmented control styling?

Beginner here with a beginner question:
I have 3 segmented controls in my view controller and I have 3 nearly identical functions that sets their colors/styling/animation. I know I shouldn't repeat code, but I'm not entirely sure how to combine these. It's prob something obvious but I need to see it done so I can do it other parts of my app. Could someone show me for this example:
class Example: UIViewController {
#IBOutlet weak var segmentedControl: UISegmentedControl!
#IBOutlet weak var textSegmentedControl: UISegmentedControl!
#IBOutlet weak var translationSegmentedControl: UISegmentedControl!
override func viewDidLoad() {
super.viewDidLoad()
setMainSegmentedControlStyle()
setTextSegmentedControlStyle()
setTranslationSegmentedControlStyle()
}
func setTextSegmentedControlStyle() {
textSegmentedControl.backgroundColor = .clear
textSegmentedControl.tintColor = .clear
textSegmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.lightGray
], for: .normal)
textSegmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16),
NSAttributedStringKey.foregroundColor: UIColor.darkGray
], for: .selected)
let textSegmentedUnderline = UIView()
textSegmentedUnderline.translatesAutoresizingMaskIntoConstraints = false // false since we are using auto layout constraints
textSegmentedUnderline.backgroundColor = #colorLiteral(red: 0.992502749, green: 0.532302916, blue: 0.08773707598, alpha: 1)
view.addSubview(textSegmentedUnderline)
textSegmentedUnderline.topAnchor.constraint(equalTo: textSegmentedControl.bottomAnchor).isActive = true
textSegmentedUnderline.heightAnchor.constraint(equalToConstant: 3).isActive = true
textSegmentedUnderline.leftAnchor.constraint(equalTo: textSegmentedControl.leftAnchor).isActive = true
textSegmentedUnderline.widthAnchor.constraint(equalTo: textSegmentedControl.widthAnchor, multiplier: 1 / CGFloat(textSegmentedControl.numberOfSegments)).isActive = true
UIView.animate(withDuration: 0.3) {
self.textSegmentedUnderline.frame.origin.x = (self.textSegmentedControl.frame.width / CGFloat(self.textSegmentedControl.numberOfSegments)) * CGFloat(self.textSegmentedControl.selectedSegmentIndex)
}
}
func setTranslationSegmentedControlStyle() {
translationSegmentedControl.backgroundColor = .clear
translationSegmentedControl.tintColor = .clear
translationSegmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.lightGray
], for: .normal)
translationSegmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.darkGray
], for: .selected)
let translationSegmentedUnderline = UIView()
translationSegmentedUnderline.translatesAutoresizingMaskIntoConstraints = false // false since we are using auto layout constraints
translationSegmentedUnderline.backgroundColor = #colorLiteral(red: 0.992502749, green: 0.532302916, blue: 0.08773707598, alpha: 1)
view.addSubview(translationSegmentedUnderline)
translationSegmentedUnderline.topAnchor.constraint(equalTo: textSegmentedControl.bottomAnchor).isActive = true
translationSegmentedUnderline.heightAnchor.constraint(equalToConstant: 3).isActive = true
translationSegmentedUnderline.leftAnchor.constraint(equalTo: translationSegmentedControl.leftAnchor).isActive = true
translationSegmentedUnderline.widthAnchor.constraint(equalTo: translationSegmentedControl.widthAnchor, multiplier: 1 / CGFloat(translationSegmentedControl.numberOfSegments)).isActive = true
UIView.animate(withDuration: 0.3) {
self.translationSegmentedUnderline.frame.origin.x = (self.textSegmentedControl.frame.width / CGFloat(self.translationSegmentedControl.numberOfSegments)) * CGFloat(self.translationSegmentedControl.selectedSegmentIndex)
}
}
func setMainSegmentedControlStyle() {
segmentedControl.backgroundColor = .clear
segmentedControl.tintColor = .clear
segmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16), NSAttributedStringKey.foregroundColor: UIColor.lightGray
], for: .normal)
segmentedControl.setTitleTextAttributes([NSAttributedStringKey.font : UIFont.systemFont(ofSize: 16),
NSAttributedStringKey.foregroundColor: UIColor.darkGray
], for: .selected)
let buttonBar = UIView()
buttonBar.translatesAutoresizingMaskIntoConstraints = false // false since we are using auto layout constraints
buttonBar.backgroundColor = #colorLiteral(red: 0.992502749, green: 0.532302916, blue: 0.08773707598, alpha: 1)
view.addSubview(buttonBar)
buttonBar.topAnchor.constraint(equalTo: segmentedControl.bottomAnchor).isActive = true
buttonBar.heightAnchor.constraint(equalToConstant: 3).isActive = true
buttonBar.leftAnchor.constraint(equalTo: segmentedControl.leftAnchor).isActive = true
buttonBar.widthAnchor.constraint(equalTo: segmentedControl.widthAnchor, multiplier: 1 / CGFloat(segmentedControl.numberOfSegments)).isActive = true
UIView.animate(withDuration: 0.3) {
self.buttonBar.frame.origin.x = (self.segmentedControl.frame.width / CGFloat(self.segmentedControl.numberOfSegments)) * CGFloat(self.segmentedControl.selectedSegmentIndex)
}
}
}
If all the styling code is exactly the same, you can declare a function that accepts a control as an argument:
func style(control: UISegmentedControl) {
control.backgroundColor = .clear
// continue styling here
}
Then simply call that function however many times you need, passing in each control:
style(control: segmentedControl)
style(control: textSegmentedControl)
style(control: translationSegmentedControl)
Note, though, that you should set as much as you can (such as backgroundColor) in your Storyboard rather than code.

Setting up UI constraint (Image and stackView inside a UIView) programmatically in Swift

I'm trying to build a custom AD when the app opens it pop up some UIViews and image and two buttons then control it from my Firebase, for now I have problem adding the adImage and buttonsStack(contains 2 buttons) inside my backView programmatically and so far nothing works ..
I need the image takes ~ %75 of the backView up and the buttonsStack ~ %25 of the rest
here some code and I have upload it to my GitHub
import UIKit
class ViewController: UIViewController {
let backroundView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .black
view.alpha = 0.5
return view
}()
let backView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .white
view.layer.cornerRadius = 15
return view
}()
let adImage: UIImageView = {
var image = UIImageView()
image.translatesAutoresizingMaskIntoConstraints = false
image.contentMode = .scaleAspectFill
return image
}()
let buttonsStack: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.alignment = UIStackViewAlignment.fill
stack.axis = UILayoutConstraintAxis.vertical
stack.distribution = .equalSpacing
stack.spacing = 8
stack.backgroundColor = UIColor.red
return stack
}()
let actionButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Open", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = UIColor(red: 0, green: 0.60, blue: 1, alpha: 1)
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
let dismessButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Exit", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .lightGray
button.layer.cornerRadius = 8
button.titleLabel?.adjustsFontSizeToFitWidth = true
button.titleLabel?.textAlignment = .center
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
func setupUI(){
// backroundView
view.addSubview(backroundView)
backroundView.frame = view.frame
// backView
view.addSubview(backView)
backView.topAnchor.constraint(equalTo: view.topAnchor, constant: 80).isActive = true
backView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -50).isActive = true
backView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -25).isActive = true
backView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 25).isActive = true
// adImage
backView.addSubview(adImage)
adImage.image = UIImage(named: "testImage")
adImage.topAnchor.constraint(equalTo: backView.topAnchor).isActive = true
adImage.rightAnchor.constraint(equalTo: backView.rightAnchor).isActive = true
adImage.leftAnchor.constraint(equalTo: backView.leftAnchor).isActive = true
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
// buttonsStack
buttonsStack.addArrangedSubview(actionButton)
buttonsStack.addArrangedSubview(dismessButton)
backView.addSubview(buttonsStack)
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
buttonsStack.bottomAnchor.constraint(equalTo: backView.bottomAnchor, constant: -15).isActive = true
buttonsStack.rightAnchor.constraint(equalTo: backView.rightAnchor, constant: -15).isActive = true
buttonsStack.leftAnchor.constraint(equalTo: backView.leftAnchor, constant: 15).isActive = true
}
}
For the image to take 0.75 change this
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.50).isActive = true
to
adImage.heightAnchor.constraint(equalTo: backView.heightAnchor, multiplier: 0.75).isActive = true
//
then the buttonStack should goes under it so change this
buttonsStack.topAnchor.constraint(equalTo: backView.topAnchor, constant: 15).isActive = true
to
buttonsStack.topAnchor.constraint(equalTo: adImage.bottomAnchor, constant: 15).isActive = true

Creating an Input Accessory View programmatically

I'm trying to create a keyboard accessory view programmatically. I've set up a container view and inside that I'm trying to set up a textfield, post button, and an emoji.
Here's an example of what I'm trying to make.
Click here to view the image.
Here's the code that I am working with. I think the problem is when I'm setting the constraints.
Couple of questions running through my mind are:
Do I need to set up constraints for the container view?
How do I add appropriate constraints to the textfield?
override var inputAccessoryView: UIView? {
get {
//Set up the container
let containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let textField = UITextField()
textField.placeholder = "Add a reframe..."
textField.isSecureTextEntry = false
textField.textAlignment = .left
textField.borderStyle = .none
textField.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor).isActive = true
textField.trailingAnchor.constraint(equalTo: containerView.trailingAnchor).isActive = true
textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor).isActive = true
textField.topAnchor.constraint(equalTo: containerView.topAnchor).isActive = true
textField.heightAnchor.constraint(equalToConstant: 50)
containerView.addSubview(textField)
return containerView
}
}
This is the error that I keep getting.
*** Terminating app due to uncaught exception 'NSGenericException', reason: 'Unable to activate constraint with anchors and because they have no common ancestor. Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
EDIT:
View Post Controller
lazy var containerView: CommentInputAccessoryView = {
let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let commentInputAccessoryView = CommentInputAccessoryView(frame: frame)
commentInputAccessoryView.delegate = self
return commentInputAccessoryView
}()
//Setting up the keyboard accessory view for comments.
override var inputAccessoryView: UIView? {
get {
return containerView
}
}
override var canBecomeFirstResponder: Bool {
return true
}
CommentInputAccessoryView
protocol CommentInputAccessoryViewDelegate {
func didSend(for comment: String)
}
class CommentInputAccessoryView: UIView {
var delegate: CommentInputAccessoryViewDelegate?
func clearCommentTextView() {
commentTextView.text = nil
showPlaceholderLabel()
}
let commentTextView: UITextView = {
let text = UITextView()
text.translatesAutoresizingMaskIntoConstraints = false
//text.placeholder = "Add a reframe..."
text.textAlignment = .left
text.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
text.layer.cornerRadius = 50/2
text.layer.masksToBounds = true
text.isScrollEnabled = false
text.font = UIFont.systemFont(ofSize: 16)
text.textContainerInset = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 64)
//text.borderStyle = .none
return text
}()
let placeholderLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "Add a response..."
label.textColor = .black
label.font = UIFont.systemFont(ofSize: 16)
return label
}()
func showPlaceholderLabel() {
placeholderLabel.isHidden = false
}
let sendButton: UIButton = {
let send = UIButton(type: .system)
//let sendButton = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
send.translatesAutoresizingMaskIntoConstraints = false
send.setTitle("Send", for: .normal)
send.setTitleColor(#colorLiteral(red: 0.2901960784, green: 0.3725490196, blue: 0.937254902, alpha: 1), for: .normal)
send.contentEdgeInsets = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 10)
send.addTarget(self, action: #selector(handlePostComment), for: .touchUpInside)
return send
}()
let hugButton: UIButton = {
let hug = UIButton()
hug.translatesAutoresizingMaskIntoConstraints = false
hug.setTitle("🤗", for: .normal)
hug.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
hug.contentEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
hug.layer.cornerRadius = 25
hug.layer.masksToBounds = true
return hug
}()
override init(frame: CGRect) {
super.init(frame: frame)
autoresizingMask = .flexibleHeight
addSubview(commentTextView)
addSubview(sendButton)
addSubview(hugButton)
addSubview(placeholderLabel)
if #available(iOS 11.0, *) {
commentTextView.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
hugButton.bottomAnchor.constraint(equalTo: safeAreaLayoutGuide.bottomAnchor, constant: -10).isActive = true
}
else {
}
commentTextView.leadingAnchor.constraint(equalTo: safeAreaLayoutGuide.leadingAnchor, constant: 8).isActive = true
commentTextView.topAnchor.constraint(equalTo: self.topAnchor, constant: 10).isActive = true
commentTextView.trailingAnchor.constraint(equalTo: hugButton.leadingAnchor, constant: 8)
placeholderLabel.leadingAnchor.constraint(equalTo: commentTextView.leadingAnchor, constant: 18).isActive = true
placeholderLabel.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true
sendButton.trailingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: -10).isActive = true
sendButton.centerYAnchor.constraint(equalTo: self.commentTextView.bottomAnchor, constant: -22).isActive = true
hugButton.leadingAnchor.constraint(equalTo: self.commentTextView.trailingAnchor, constant: 10).isActive = true
hugButton.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -8).isActive = true
hugButton.widthAnchor.constraint(equalToConstant: 40)
//hugButton.centerYAnchor.constraint(equalTo: self.commentTextView.centerYAnchor).isActive = true
NotificationCenter.default.addObserver(self, selector: #selector(handleTextChanged), name: .UITextViewTextDidChange, object: nil)
}
override var intrinsicContentSize: CGSize {
return .zero
}
#objc func handleTextChanged() {
placeholderLabel.isHidden = !self.commentTextView.text.isEmpty
}
#objc func handlePostComment() {
guard let commentText = commentTextView.text else {return}
delegate?.didSend(for: commentText)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Here are some photos that might help for what is happening.
InputAccessoryView working:
Click here
TextView expansion:
Click here.
Before applying constraints view should be in view hierarchy or error which you got will be raised. To get rid of error just do containerView.addSubview(textField) after let textField = UITextField().
Regarding example image you posted, initial solution could be something like this
override var inputAccessoryView: UIView? {
get {
//Set up the container
let containerView = UIView()
containerView.backgroundColor = #colorLiteral(red: 0.9784782529, green: 0.9650371671, blue: 0.9372026324, alpha: 1)
containerView.frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 60)
let textField = UITextField()
containerView.addSubview(textField)
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Add a reframe..."
textField.textAlignment = .left
textField.backgroundColor = .white
textField.layer.cornerRadius = 50/2
textField.layer.masksToBounds = true
textField.borderStyle = .none
textField.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8).isActive = true
textField.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -5).isActive = true
textField.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 10).isActive = true
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: textField.frame.height)) // adding left padding so it's not sticked to border
textField.leftViewMode = .always
let arrow = UIImageView(image: #imageLiteral(resourceName: "arrowUp"))
containerView.addSubview(arrow)
arrow.translatesAutoresizingMaskIntoConstraints = false
arrow.trailingAnchor.constraint(equalTo: textField.trailingAnchor, constant: -10).isActive = true
arrow.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
let button = UIButton()
containerView.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("🤗", for: .normal)
button.contentEdgeInsets = UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5)
button.leadingAnchor.constraint(equalTo: textField.trailingAnchor, constant: 10).isActive = true
button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
button.centerYAnchor.constraint(equalTo: textField.centerYAnchor).isActive = true
// Negative values for constraints can be avoided if we change order of views when applying constrains
// f.e. instead of button.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true
// write containerView.trailingAnchor.constraint(equalTo: button.trailingAnchor, constant: 8).isActive = true
return containerView
}
}

Resources