How to apply multiplier to a var bottom constraint - swift

My code below produces the image below.
What I want is the height of the UIView to be exactly half of the view controller user something like:
constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, multiplier: 1/2)
I dont know where to put it since I am creating a var for every side.
import UIKit
class ViewController: UIViewController {
var myView = UIView()
var topConstraint: NSLayoutConstraint!
var bottomConstraint: NSLayoutConstraint!
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var jessicaAlba:Float = 50
override func viewDidLoad() {
super.viewDidLoad()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = UIColor.orange
view.addSubview(myView)
topConstraint = myView.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(jessicaAlba))
topConstraint.isActive = true
bottomConstraint = myView.bottomAnchor.constraint(equalTo: view.bottomAnchor , constant: CGFloat(-jessicaAlba))
bottomConstraint.isActive = true
leadingConstraint = myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: CGFloat(jessicaAlba))
leadingConstraint.isActive = true
trailingConstraint = myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-jessicaAlba))
trailingConstraint.isActive = true
}
}

You should use heightConstraint instead of bottomConstraint to get that done. Try the snippet below.
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
var myView = UIView()
var topConstraint: NSLayoutConstraint!
var heightConstraint: NSLayoutConstraint!
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var jessicaAlba:Float = 50
override func viewDidLoad() {
super.viewDidLoad()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = UIColor.orange
view.addSubview(myView)
topConstraint = myView.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(jessicaAlba))
topConstraint.isActive = true
heightConstraint = myView.heightAnchor.constraint(equalTo: view.heightAnchor , multiplier: 0.5, constant: CGFloat(-jessicaAlba))
heightConstraint.isActive = true
leadingConstraint = myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: CGFloat(jessicaAlba))
leadingConstraint.isActive = true
trailingConstraint = myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-jessicaAlba))
trailingConstraint.isActive = true
}
}

Related

UITextFields keyboard not appearing in UIScrollview

I have 2 textfields as subViews of a contentView and the contentView is a subView of a scrollView. The textfields appear with no issue.But the problem I am having is when I tap or select the textfield to enter text neither a keyboard appears or the blue cursor, the textfields placeholder remains there. Is there something wrong with my constraints? How can I get the keyboard to appear and edit the text?
class SignInViewController: UIViewController {
private let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
private let contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private let emailOrPhoneTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Email or Phone Number"
textField.textAlignment = .center
textField.backgroundColor = .red
return textField
}()
private let passwordTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.placeholder = "Password"
textField.backgroundColor = .yellow
return textField
}()
override func viewDidLoad() {
super.viewDidLoad()
setupScrollView()
setupTextFields()
}
}
extension SignInViewController{
private func setupScrollView(){
view.addSubview(scrollView)
scrollView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
scrollView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollView.addSubview(contentView)
contentView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
}
private func setupTextFields(){
contentView.addSubview(emailOrPhoneTextField)
contentView.addSubview(passwordTextField)
emailOrPhoneTextField.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
emailOrPhoneTextField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 25).isActive = true
emailOrPhoneTextField.widthAnchor.constraint(equalTo: contentView.widthAnchor).isActive = true
emailOrPhoneTextField.heightAnchor.constraint(equalToConstant: 44).isActive = true
passwordTextField.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
passwordTextField.topAnchor.constraint(equalTo: emailOrPhoneTextField.bottomAnchor, constant: 25).isActive = true
passwordTextField.widthAnchor.constraint(equalTo: contentView.widthAnchor).isActive = true
passwordTextField.heightAnchor.constraint(equalToConstant: 44).isActive = true
}
}
Try this and check if it works:
extension ViewController{
private func setupScrollView(){
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(emailOrPhoneTextField)
contentView.addSubview(passwordTextField)
NSLayoutConstraint.activate([
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor)
])
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor),
contentView.rightAnchor.constraint(equalTo: scrollView.rightAnchor),
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
contentView.leftAnchor.constraint(equalTo: scrollView.leftAnchor),
contentView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
contentView.heightAnchor.constraint(equalTo: scrollView.heightAnchor)
])
NSLayoutConstraint.activate([
emailOrPhoneTextField.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 50),
emailOrPhoneTextField.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20),
emailOrPhoneTextField.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 20),
])
NSLayoutConstraint.activate([
passwordTextField.topAnchor.constraint(equalTo: emailOrPhoneTextField.bottomAnchor, constant: 25),
passwordTextField.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -20),
passwordTextField.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 20),
])
}
}

add 2 image views to a uiscrollview func every time it is called

My swift code below goal is to add 2 image views every time. Ass you can in the gif below only one image view is being added. I just need to add 2 image views. The image views are lastImage and lastImage2. you can see only lastImage is being shown. It seems I can only add 1 imageview when func didclickadd is called.
import UIKit
class ViewController: UIViewController {
fileprivate var lastImage:UIImageView?
fileprivate var lastImage2:UIImageView?
fileprivate var mainViewBootom:NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupVIew()
}
override func viewDidAppear(_ animated: Bool) {
scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
view.layoutIfNeeded()
}
//MARK: Components
let scrollView:UIScrollView = {
let sv = UIScrollView(frame: .zero)
return sv
}()
let mainView:UIView = {
let uv = UIView()
uv.backgroundColor = .white
return uv
}()
let btnAdd:UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Add", for: .normal)
return btn
}()
let textField:UITextField = {
let jake = UITextField()
return jake
}()
//MARK: Setup UI
func setupVIew() {
view.addSubview(scrollView)
view.addSubview(btnAdd)
view.addSubview(textField)
scrollView.translatesAutoresizingMaskIntoConstraints = false
btnAdd.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
btnAdd.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnAdd.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
btnAdd.widthAnchor.constraint(equalToConstant: 100),
btnAdd.heightAnchor.constraint(equalToConstant: 45),
//
textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 25),
textField.widthAnchor.constraint(equalToConstant: 100),
textField.heightAnchor.constraint(equalToConstant: 45),
//
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: btnAdd.topAnchor , constant: -12),
])
btnAdd.addTarget(self, action: #selector(didClickedAdd), for: .touchUpInside)
scrollView.addSubview(mainView)
mainView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
mainView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
])
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 100))
imgView.backgroundColor = .red
mainView.addSubview(imgView)
let samsam = UIImageView(frame: CGRect(x: 0, y: 200, width: 40, height: 100))
samsam.backgroundColor = .blue
mainView.addSubview(samsam)
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.widthAnchor.constraint(equalToConstant: 150).isActive = true
imgView.heightAnchor.constraint(equalToConstant: 100).isActive = true
samsam.translatesAutoresizingMaskIntoConstraints = false
samsam.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
samsam.topAnchor.constraint(equalTo: imgView.bottomAnchor).isActive = true
samsam.widthAnchor.constraint(equalToConstant: 75).isActive = true
samsam.heightAnchor.constraint(equalToConstant: 100).isActive = true
if lastImage != nil {
imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 20).isActive = true
}else{
imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 12).isActive = true
}
lastImage = samsam
mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 12)
mainViewBootom!.isActive = true
}
#objc func didClickedAdd(){
let imgView = UIImageView(frame: CGRect(x: 20, y: 0, width: 30, height: 20))
imgView.backgroundColor = .orange
mainView.addSubview(imgView)
let ss = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 50))
imgView.backgroundColor = .green
mainView.addSubview(ss)
imgView.translatesAutoresizingMaskIntoConstraints = false
imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.widthAnchor.constraint(equalToConstant: 40).isActive = true
imgView.heightAnchor.constraint(equalToConstant: 60).isActive = true
ss.translatesAutoresizingMaskIntoConstraints = false
ss.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = false
ss.widthAnchor.constraint(equalToConstant: 80).isActive = true
ss.heightAnchor.constraint(equalToConstant: 90).isActive = true
if lastImage != nil {
ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true
imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 50).isActive = true
}else{
imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 10).isActive = true
ss.bottomAnchor.constraint(equalTo: imgView.bottomAnchor , constant: 25).isActive = true
}
lastImage = imgView
lastImage2 = ss
mainView.removeConstraint(mainViewBootom!)
mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage2!.bottomAnchor , constant: 40)
mainViewBootom!.isActive = true
view.layoutIfNeeded()
scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
view.layoutIfNeeded()
}
}
Couple notes...
With proper constraint setup, auto-layout handles the UIScrollView content size all by itself. No need to ever set scrollView.contentSize = ...
You have several instances of adding a subview (image view) to your mainView, which is a subview of your scroll view, but then you add constraints from that subview to your controller's view. Make sure you are constraining elements to the proper other elements.
Here's your code, with commented changes:
class BenViewController: UIViewController {
fileprivate var lastImage:UIImageView?
// 1) don't need this
// fileprivate var lastImage2:UIImageView?
fileprivate var mainViewBootom:NSLayoutConstraint?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupVIew()
}
// 2) don't need this
// override func viewDidAppear(_ animated: Bool) {
// scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
// view.layoutIfNeeded()
// }
//MARK: Components
let scrollView:UIScrollView = {
let sv = UIScrollView(frame: .zero)
return sv
}()
let mainView:UIView = {
let uv = UIView()
uv.backgroundColor = .white
return uv
}()
let btnAdd:UIButton = {
let btn = UIButton(type: .system)
btn.setTitle("Add", for: .normal)
return btn
}()
let textField:UITextField = {
let jake = UITextField()
return jake
}()
//MARK: Setup UI
func setupVIew() {
view.addSubview(scrollView)
view.addSubview(btnAdd)
view.addSubview(textField)
scrollView.translatesAutoresizingMaskIntoConstraints = false
btnAdd.translatesAutoresizingMaskIntoConstraints = false
textField.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
btnAdd.centerXAnchor.constraint(equalTo: view.centerXAnchor),
btnAdd.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -12),
btnAdd.widthAnchor.constraint(equalToConstant: 100),
btnAdd.heightAnchor.constraint(equalToConstant: 45),
//
textField.centerXAnchor.constraint(equalTo: view.centerXAnchor),
textField.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: 25),
textField.widthAnchor.constraint(equalToConstant: 100),
textField.heightAnchor.constraint(equalToConstant: 45),
//
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: btnAdd.topAnchor , constant: -12),
])
btnAdd.addTarget(self, action: #selector(didClickedAdd), for: .touchUpInside)
scrollView.addSubview(mainView)
mainView.translatesAutoresizingMaskIntoConstraints = false
// 3) change this:
// NSLayoutConstraint.activate([
// mainView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
// mainView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
// ])
//
// to this
NSLayoutConstraint.activate([
mainView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
mainView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
mainView.topAnchor.constraint(equalTo: scrollView.topAnchor),
mainView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
mainView.widthAnchor.constraint(equalTo: scrollView.widthAnchor),
])
// end of change 3)
let imgView = UIImageView(frame: CGRect(x: 0, y: 0, width: 150, height: 100))
imgView.backgroundColor = .red
mainView.addSubview(imgView)
let samsam = UIImageView(frame: CGRect(x: 0, y: 200, width: 40, height: 100))
samsam.backgroundColor = .blue
mainView.addSubview(samsam)
imgView.translatesAutoresizingMaskIntoConstraints = false
// 4) change view.centerXAnchor to mainView.centerXAnchor
// imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true
imgView.widthAnchor.constraint(equalToConstant: 150).isActive = true
imgView.heightAnchor.constraint(equalToConstant: 100).isActive = true
samsam.translatesAutoresizingMaskIntoConstraints = false
// 5) change view.centerXAnchor to mainView.centerXAnchor
// samsam.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
samsam.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true
samsam.topAnchor.constraint(equalTo: imgView.bottomAnchor).isActive = true
samsam.widthAnchor.constraint(equalToConstant: 75).isActive = true
samsam.heightAnchor.constraint(equalToConstant: 100).isActive = true
if lastImage != nil {
imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 20).isActive = true
}else{
imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 12).isActive = true
}
lastImage = samsam
mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 12)
mainViewBootom!.isActive = true
}
#objc func didClickedAdd(){
let imgView = UIImageView(frame: CGRect(x: 20, y: 0, width: 30, height: 20))
imgView.backgroundColor = .orange
mainView.addSubview(imgView)
let ss = UIImageView(frame: CGRect(x: 0, y: 0, width: 40, height: 50))
// 6) typo or copy/paste mistake
// imgView.backgroundColor = .green
ss.backgroundColor = .green
mainView.addSubview(ss)
imgView.translatesAutoresizingMaskIntoConstraints = false
// 7) change view.centerXAnchor to mainView.centerXAnchor
// imgView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
imgView.centerXAnchor.constraint(equalTo: mainView.centerXAnchor).isActive = true
imgView.widthAnchor.constraint(equalToConstant: 40).isActive = true
imgView.heightAnchor.constraint(equalToConstant: 60).isActive = true
ss.translatesAutoresizingMaskIntoConstraints = false
// 8) change view.leadingAnchor to mainView.leadingAnchor
// ss.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = false
ss.leadingAnchor.constraint(equalTo: mainView.leadingAnchor).isActive = false
ss.widthAnchor.constraint(equalToConstant: 80).isActive = true
ss.heightAnchor.constraint(equalToConstant: 90).isActive = true
// 9) always need to do this ... but did you mean imgView.bottomAnchor?
ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true
if lastImage != nil {
// 9a) instead of only here
//ss.topAnchor.constraint(equalTo: imgView.topAnchor , constant: 20).isActive = true
imgView.topAnchor.constraint(equalTo: lastImage!.bottomAnchor , constant: 50).isActive = true
}else{
imgView.topAnchor.constraint(equalTo: mainView.topAnchor , constant: 10).isActive = true
}
// 10) always need to do this
// deactivate bottom constraint
mainViewBootom?.isActive = false
lastImage = ss
mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage!.bottomAnchor, constant: 40)
mainViewBootom?.isActive = true
// 11) don't need any of this
// lastImage = imgView
// lastImage2 = ss
// mainView.removeConstraint(mainViewBootom!)
//
//
// mainViewBootom = mainView.bottomAnchor.constraint(equalTo: lastImage2!.bottomAnchor , constant: 40)
//
//
//
//
// mainViewBootom!.isActive = true
// view.layoutIfNeeded()
//
// scrollView.contentSize = CGSize(width: view.frame.width, height: mainView.frame.height)
// view.layoutIfNeeded()
}
}
Use Xcode’s “view debugger” (the button is circled in red in my screen snapshot below) and you’ll see what’s going on:
Your ss view has no background color. Note, that when you created that view, you accidentally reset the imgView background color a second time rather than setting the ss.backgroundColor.
Fix that and you’ll see your both imgView and ss:
The view debugger is your best friend when trying to diagnose issues like this. Now, obviously, the green view probably isn’t where you intended it, but you should now be able to see it and diagnose that issue very easily.
All of this having been said, a few observations:
You’re making life much harder than you need to. If you just set the constraints for the scroll view and a stack view within that scroll view, you then only need to add an arranged subview. For example:
#objc func didTapButton(_ sender: UIButton) {
stackView.addArrangedSubview(randomView())
stackView.addArrangedSubview(randomView())
}
Note, once the stack view and scroll view have been set up (see below), then you don’t need to mess around with contentSize or constraints for these subviews at all (other than the widthAnchor and heightAnchor). The auto layout engine, combined with the constraints between the stack view and the scroll view, will take care of everything for you.
So, a full working example:
class ViewController: UIViewController {
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let stackView: UIStackView = {
let stackView = UIStackView()
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.axis = .vertical
stackView.alignment = .center
stackView.spacing = 10
return stackView
}()
let button: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Add", for: .normal)
button.addTarget(self, action: #selector(didTapButton(_:)), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
configure()
}
}
// MARK: - Actions
extension ViewController {
#objc func didTapButton(_ sender: UIButton) {
stackView.addArrangedSubview(randomView())
stackView.addArrangedSubview(randomView())
}
}
// MARK: - Private utility methods
private extension ViewController {
func configure() {
view.addSubview(scrollView)
view.addSubview(button)
scrollView.addSubview(stackView)
NSLayoutConstraint.activate([
// define frame of `scrollView`
scrollView.topAnchor.constraint(equalTo: view.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: button.topAnchor),
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
// define frame of `button`
button.centerXAnchor.constraint(equalTo: view.centerXAnchor),
button.bottomAnchor.constraint(equalTo: view.bottomAnchor),
// define contentSize of `scrollView` based upon size of `stackView`
stackView.topAnchor.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor),
stackView.bottomAnchor.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor),
stackView.leadingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor),
// but define width of `stackView` relative to the _main view_
stackView.widthAnchor.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor)
])
button.setContentHuggingPriority(.required, for: .vertical)
}
func randomView() -> UIView {
let widthRange = view.bounds.width * 0.1 ... view.bounds.width * 0.9
let heightRange = view.bounds.width * 0.1 ... view.bounds.width * 0.25
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
view.widthAnchor.constraint(equalToConstant: .random(in: widthRange)),
view.heightAnchor.constraint(equalToConstant: .random(in: heightRange))
])
view.backgroundColor = UIColor(red: .random(in: 0.25...1), green: .random(in: 0.25...1), blue: .random(in: 0.25...1), alpha: 1)
return view
}
}
Even better, I’d personally set up the scroll view, stack view, button, and all the associated constraints in Interface Builder, and then that hairy configure method in my example goes away completely. It’s fun to learn how to create views programmatically, but in real-world projects, it’s rarely the most productive way to do it. Do programmatic views where needed (e.g. adding arranged subviews to the stack view on the click of a button), but otherwise, for those views that should be there when you first run the app, Interface Builder is worth considering.
E.g. It dramatically reduces the amount of code above, leaving us simply with:
class ViewController: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
#IBOutlet weak var stackView: UIStackView!
#IBAction func didTapButton(_ sender: UIButton) {
stackView.addArrangedSubview(randomView())
stackView.addArrangedSubview(randomView())
}
}
// MARK: - Private utility methods
private extension ViewController {
func randomView() -> UIView { ... }
}
Clearly, it takes a while to get used to designing views and configuring constraints in IB, but it’s worth the effort. It distills our code down the the bare essentials.
In your code, you’re setting frames for these image views and then setting translatesAutoresizingMaskIntoConstraints. There’s absolutely no point in setting the frame in that case, because translatesAutoresizingMaskIntoConstraints says “ignore my frame, use constraints instead.”
I’m assuming you’re doing all of this just to become familiar with scroll views, but it’s worth noting that, especially when adding lots of image views, that the scroll view is an inherently inefficient approach.
For example, let’s say you’ve added 100 image views, but you can see only 8 at a time. Do you really want to hold all 100 image views in memory at the same time? No.
But, UITableView, which is a subclass of UIScrollView, takes care of this. You end up only keeping the currently visible image views in memory. It’s a far better approach.
This is especially true when you start using actual UIImage objects, because they require a lot of memory. We get lulled into a sense of security, looking at reasonably sized PNG/JPG assets, but when they’re loaded into memory, they’re uncompressed and require a disproportionate amount of memory.

Use slider to manipulate anchors constrained by a multiplier in swift

I want to use a slider to make pic imageview smaller. Right now its in its max size. The slider should be able to make it smaller. When I use the slider the pic does decrease but I cant increase it, I can only decrease it. This is a pic of what I am looking to do.
My code is in all code no storyboard used.
import UIKit
class ViewController: UIViewController {
var sd = UISlider()
var pic = UIImageView()
var start = [NSLayoutConstraint]()
var change = [NSLayoutConstraint]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[sd,pic].forEach{
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
$0.backgroundColor = .systemOrange
}
pic.topAnchor.constraint(equalTo: view.topAnchor, constant: 12).isActive = true
pic.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
pic.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
start = [
pic.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pic.heightAnchor.constraint(equalTo: view.layoutMarginsGuide.heightAnchor, multiplier: 1/2),
sd.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :150),
sd.topAnchor.constraint(equalTo: view.centerYAnchor, constant : +250),
sd.widthAnchor.constraint(equalToConstant: 150),
sd.heightAnchor.constraint(equalToConstant: 40)
]
NSLayoutConstraint.activate(start)
sd.addTarget(self, action: #selector(moveRight), for: .allTouchEvents)
}
#objc func moveRight(_ sender: UISlider) {
NSLayoutConstraint.deactivate(start)
let jessicaAlba = 300 + ( 300 * CGFloat(sd.value) )
print(CGFloat(sd.value))
pic.topAnchor.constraint(equalTo: view.topAnchor, constant: -jessicaAlba).isActive = true
pic.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: jessicaAlba).isActive = true
pic.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -jessicaAlba).isActive = true
start = [
pic.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pic.heightAnchor.constraint(equalTo: view.layoutMarginsGuide.heightAnchor, multiplier: 1/2),
sd.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :150),
sd.topAnchor.constraint(equalTo: view.centerYAnchor, constant : +250),
sd.widthAnchor.constraint(equalToConstant: 100),
sd.heightAnchor.constraint(equalToConstant: 40)
]
NSLayoutConstraint.activate (start)
}
}
Apologies for the very crude code, but this works:
var myView = UIView()
var topConstraint: NSLayoutConstraint!
var bottomConstraint: NSLayoutConstraint!
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var jessicaAlba:Float = 50
var slider = UISlider()
override func viewDidLoad() {
super.viewDidLoad()
myView.translatesAutoresizingMaskIntoConstraints = false
myView.backgroundColor = UIColor.orange
view.addSubview(myView)
slider.translatesAutoresizingMaskIntoConstraints = false
slider.minimumValue = 50
slider.maximumValue = 200
slider.setValue(jessicaAlba, animated: false)
slider.addTarget(self, action: #selector(changeSize), for: .valueChanged)
view.addSubview(slider)
slider.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
slider.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
slider.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
topConstraint = myView.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(jessicaAlba))
topConstraint.isActive = true
bottomConstraint = myView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: CGFloat(-jessicaAlba))
bottomConstraint.isActive = true
leadingConstraint = myView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: CGFloat(jessicaAlba))
leadingConstraint.isActive = true
trailingConstraint = myView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-jessicaAlba))
trailingConstraint.isActive = true
}
#objc func changeSize() {
jessicaAlba = slider.value
topConstraint.constant = CGFloat(jessicaAlba)
bottomConstraint.constant = CGFloat(-jessicaAlba)
leadingConstraint.constant = CGFloat(jessicaAlba)
trailingConstraint.constant = CGFloat(-jessicaAlba)
}
The main thing is to:
declare the specific constraints you want to change
set isActive = true in a separate line (I know, stupid but the compiler doesn't recognize the NSLayoutConstraint type and will not build if you do it on a single line
change the constraint as needed

.tag on slider not changing the alpha of imagview

My swift code below has 2 different buttons which should effect the slider baised on the .tagnumber they initialize. B1 should effect the alhpa or transpency of the imageivew and b2 should decrease / increase the size. What b2 does works. What B1 does not work.My code does not use any storyboards. Also the uislider should only do one task it cannont resize the imageview and change the alpha at the same time. Only 1 task.
import UIKit
class ViewController: UIViewController {
var pzc = UIImageView()
var s = UISlider()
var b1 = UIButton()
var b2 = UIButton()
var jessicaAlba:Float = 50
var topConstraint: NSLayoutConstraint!
var heightConstraint: NSLayoutConstraint!
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
[pzc,s,b1,b2].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
view.addSubview($0)
}
[b1,b2].forEach {
$0.backgroundColor = .systemRed
}
pzc.backgroundColor = .systemGray
b1.frame = CGRect(x: view.center.x-115, y: view.center.y+200, width: 30, height: 30)
b2.frame = CGRect(x: view.center.x-115, y: view.center.y+250, width: 30, height: 30)
s.addTarget(self, action: #selector(moveRight), for: .touchUpInside)
b1.addTarget(self, action: #selector(mr1), for: .touchUpInside)
b2.addTarget(self, action: #selector(mr2), for: .touchUpInside)
NSLayoutConstraint.activate ([
b1.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :37.5),
b1.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 225),
b1.widthAnchor.constraint(equalToConstant: 75),
b1.heightAnchor.constraint(equalToConstant: 50),
b2.trailingAnchor.constraint(equalTo: view.centerXAnchor, constant :130),
b2.topAnchor.constraint(equalTo: view.centerYAnchor, constant : 225),
b2.widthAnchor.constraint(equalToConstant: 75),
b2.heightAnchor.constraint(equalToConstant: 50),
])
s.minimumValue = 50
s.maximumValue = 200
s.setValue(jessicaAlba, animated: false)
view.addSubview(s)
s.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
s.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
s.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -10).isActive = true
pzc.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pzc)
topConstraint = pzc.topAnchor.constraint(equalTo: view.topAnchor, constant: CGFloat(jessicaAlba))
topConstraint.isActive = true
heightConstraint = pzc.heightAnchor.constraint(equalTo: view.heightAnchor , multiplier: 0.5, constant: CGFloat(-jessicaAlba))
heightConstraint.isActive = true
leadingConstraint = pzc.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: CGFloat(jessicaAlba))
leadingConstraint.isActive = true
trailingConstraint = pzc.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: CGFloat(-jessicaAlba))
trailingConstraint.isActive = true
}
#objc func moveRight() {
if s.tag == 1 {
pzc.alpha = CGFloat(s.value)
}
if s.tag == 2 {
changeSize()
}
}
#objc func changeSize() {
UIView.animate(withDuration: 0.5, animations: {
self.jessicaAlba = self.s.value
self.topConstraint.constant = CGFloat(self.jessicaAlba)
self.heightConstraint.constant = CGFloat(-self.jessicaAlba)
self.leadingConstraint.constant = CGFloat(self.jessicaAlba)
self.trailingConstraint.constant = CGFloat(-self.jessicaAlba)
self.view.layoutIfNeeded()
}) { (finished) in
}
}
#objc func mr1() {
b1.backgroundColor = .brown
b2.backgroundColor = .systemPink
s.tag = 1
}
#objc func mr2() {
b2.backgroundColor = .brown
b1.backgroundColor = .systemPink
s.tag = 2
}
}
Here multiple target are added on your UISlider. Just modify your function.
#objc func moveRight() {
if s.tag == 1 {
let diff = s.maximumValue-s.minimumValue
pzc.alpha = CGFloat(s.value/diff)
}
if s.tag == 2 {
changeSize()
}
}
You can also modify your changeSize() function for animation.
#objc func changeSize() {
UIView.animate(withDuration: 0.5, animations: {
self.jessicaAlba = self.s.value
self.topConstraint.constant = CGFloat(self.jessicaAlba)
self.heightConstraint.constant = CGFloat(-self.jessicaAlba)
self.leadingConstraint.constant = CGFloat(self.jessicaAlba)
self.trailingConstraint.constant = CGFloat(-self.jessicaAlba)
self.view.layoutIfNeeded()
}) { (finished) in
}
}

heightAnchor.constraint not change height of view

I use this code:
func show(){
view.translatesAutoresizingMaskIntoConstraints = false
view.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
view.heightAnchor.constraint(equalToConstant: view.frame.height - 300).isActive = true
view.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
}
func hide(){
view.translatesAutoresizingMaskIntoConstraints = false
view.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
view.heightAnchor.constraint(equalToConstant: view.frame.height + 300).isActive = true
view.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
}
show function works fine, but hide function does not work and heightAnchor does not work.
First declare constraints
var heightArchonWhenShow = view.heightAnchor.constraint(equalToConstant: view.frame.height - 300)
var heightArchonWhenHide = view.heightAnchor.constraint(equalToConstant: view.frame.height + 300)
After init your constraints on ViewDidLoad
func setConstraints(){
view.translatesAutoresizingMaskIntoConstraints = false
view.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
view.topAnchor.constraint(equalTo: view.topAnchor, constant: 0).isActive = true
view.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
heightArchonWhenShow.isActive = true
}
And simple active them , don't overwrite. Like this:
func show(){
heightArchonWhenShow.isActive = true
heightArchonWhenHide.isActive = false
}
func hide(){
heightArchonWhenShow.isActive = false
heightArchonWhenHide.isActive = true
}
If u want u can also animate changes, like that :
func hide(){
heightArchonWhenShow.isActive = false
heightArchonWhenHide.isActive = true
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}
I recommend you to setup your constraint in a different function like maybe setup() and store them in some variable that you can modify after, I'll show you an example
var constraint: NSLayoutConstraint?
func setup() {
constraint = view.heightAnchor.constraint(equalToConstant: view.frame.height)
}
func show() {
if constraint != nil {
constraint!.constant = constraint!.constant + 300
}
}
func hide() {
if constraint != nil {
constraint!.constant = constraint!.constant - 300
}
}
You need only 1 var
var heightCon = view.heightAnchor.constraint(equalToConstant: view.frame.height - 300)
heightCon.isActive = true
Then manage it's constant value
func showOrHide(_ te:Bool){
heightCon.constant = te ? view.frame.height - 300 : view.frame.height + 300
UIView.animate(withDuration: 0.3) {
self.view.layoutIfNeeded()
}
}