Centered UITextField text moving unexpectedly on edit - ios

I have a centered UITextField inside a UICollectionView Cell, but the text moves weirdly to the right when editing. What is causing the problem?
let textLabel: UITextField = {
let label = UITextField()
label.textAlignment = .center
label.adjustsFontSizeToFitWidth = true
label.minimumFontSize = 12
return label
}()
addSubview(textLabel)
textLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 15).isActive = true
textLabel.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
textLabel.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
textLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -15).isActive = true
Here is a screen capture of the problem:

Update: Turns out the UITextfield's height is too large, I reduced its height and everything works perfectly now. Thanks for your kind help!
textLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 15).isActive = true
textLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
textLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
textLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -15).isActive = true

Related

Best Practices for programmatically using autolayout

I am using auto layout throughout my whole app programmatically but I am really struggling to make my App look good on all devices (especially struggling with the iPhone SE). Here is an example of my StartViewController (SE, 8 & 11 Pro Max):
As you can see the view looks pretty good on the iPhone 8 and 11 Pro Max. However on the iPhone SE it's quite bad. I don't quite get why because there would be enough space to layout all the views like in iPhone 8 ?? For some reason I think the buttons and labels are bigger (might just be an illusion).
My question is how I can fix that issue? What are best practices? Shrinking the fontSize? Making the buttons smaller ? What is the best way to get a dynamic layout that works for every iPhone? Apparently I am using Auto-Layout not in the best way...
Here is how I constrain the views from the picture:
//MARK: setupViews
func setUpViews(){
view.addSubview(backgroundImage)
view.addSubview(willkommenLabel)
view.addSubview(textLabel)
view.addSubview(emailButton)
emailButton.addSubview(emailImage)
view.addSubview(oderLabel)
view.addSubview(lineLeft)
view.addSubview(lineRight)
view.addSubview(facebookButton)
facebookButton.addSubview(facebookLogo)
view.addSubview(googleButton)
googleButton.addSubview(googleLogo)
view.addSubview(appleButton)
appleButton.addSubview(appleLogo)
view.addSubview(documentsLabel)
backgroundImage.topAnchor.constraint(equalTo: view.topAnchor, constant: -20).isActive = true
backgroundImage.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 20).isActive = true
backgroundImage.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -20).isActive = true
backgroundImage.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20).isActive = true
willkommenLabel.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 80).isActive = true
willkommenLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
willkommenLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
textLabel.topAnchor.constraint(equalTo: willkommenLabel.bottomAnchor, constant: 30).isActive = true
textLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
emailButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
emailButton.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
emailButton.topAnchor.constraint(equalTo: textLabel.topAnchor, constant: 100).isActive = true
emailButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
emailImage.centerYAnchor.constraint(equalTo: emailButton.centerYAnchor).isActive = true
emailImage.leadingAnchor.constraint(equalTo: emailButton.leadingAnchor, constant: 10).isActive = true
emailImage.heightAnchor.constraint(equalToConstant: 25).isActive = true
emailImage.widthAnchor.constraint(equalToConstant: 25).isActive = true
oderLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
oderLabel.bottomAnchor.constraint(equalTo: emailButton.bottomAnchor, constant: 40).isActive = true
oderLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
lineLeft.centerYAnchor.constraint(equalTo: oderLabel.centerYAnchor).isActive = true
lineLeft.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
lineLeft.trailingAnchor.constraint(equalTo: oderLabel.leadingAnchor).isActive = true
lineRight.centerYAnchor.constraint(equalTo: oderLabel.centerYAnchor).isActive = true
lineRight.leadingAnchor.constraint(equalTo: oderLabel.trailingAnchor).isActive = true
lineRight.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
facebookButton.leadingAnchor.constraint(equalTo: emailButton.leadingAnchor).isActive = true
facebookButton.trailingAnchor.constraint(equalTo: emailButton.trailingAnchor).isActive = true
facebookButton.bottomAnchor.constraint(equalTo: oderLabel.bottomAnchor, constant: 55 + 10).isActive = true
facebookButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
facebookLogo.centerYAnchor.constraint(equalTo: facebookButton.centerYAnchor).isActive = true
facebookLogo.leadingAnchor.constraint(equalTo: facebookButton.leadingAnchor, constant: 10).isActive = true
facebookLogo.heightAnchor.constraint(equalToConstant: 25).isActive = true
facebookLogo.widthAnchor.constraint(equalToConstant: 25).isActive = true
googleButton.leadingAnchor.constraint(equalTo: emailButton.leadingAnchor).isActive = true
googleButton.trailingAnchor.constraint(equalTo: emailButton.trailingAnchor).isActive = true
googleButton.bottomAnchor.constraint(equalTo: facebookButton.bottomAnchor, constant: 55 + 10).isActive = true
googleButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
googleLogo.centerYAnchor.constraint(equalTo: googleButton.centerYAnchor).isActive = true
googleLogo.leadingAnchor.constraint(equalTo: googleButton.leadingAnchor, constant: 10).isActive = true
googleLogo.heightAnchor.constraint(equalToConstant: 25).isActive = true
googleLogo.widthAnchor.constraint(equalToConstant: 25).isActive = true
appleButton.leadingAnchor.constraint(equalTo: emailButton.leadingAnchor).isActive = true
appleButton.trailingAnchor.constraint(equalTo: emailButton.trailingAnchor).isActive = true
appleButton.bottomAnchor.constraint(equalTo: googleButton.bottomAnchor, constant: 55 + 10).isActive = true
appleButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
appleLogo.centerYAnchor.constraint(equalTo: appleButton.centerYAnchor).isActive = true
appleLogo.leadingAnchor.constraint(equalTo: appleButton.leadingAnchor, constant: 10).isActive = true
appleLogo.heightAnchor.constraint(equalToConstant: 25).isActive = true
appleLogo.widthAnchor.constraint(equalToConstant: 25).isActive = true
documentsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
documentsLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
documentsLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -5).isActive = true
}
Give this a try.
It uses a few percentage heights (based on your original layout on an iPhone 8 screen).
I didn't change any of your existing code. Just add the following func and change your call from:
setupViews()
to
setupViewsDon()
Should be clear from the comments where you might want to make any adjustments... but hopefully this will get you close to your goal - and maybe you'll find a few tips for future use:
func setupViewsDon(){
// setting these properties here, so I don't have to change your original initialization
willkommenLabel.numberOfLines = 1
willkommenLabel.adjustsFontSizeToFitWidth = true
willkommenLabel.minimumScaleFactor = 0.5
textLabel.numberOfLines = 2
textLabel.adjustsFontSizeToFitWidth = true
textLabel.minimumScaleFactor = 0.5
// prevent willkommenLabel from being compressed or streched
willkommenLabel.setContentHuggingPriority(.required, for: .vertical)
willkommenLabel.setContentCompressionResistancePriority(.required, for: .vertical)
// prevent oderLabel from being compressed or streched
oderLabel.setContentHuggingPriority(.required, for: .vertical)
oderLabel.setContentCompressionResistancePriority(.required, for: .vertical)
// prevent documentsLabel from being compressed or streched
documentsLabel.setContentHuggingPriority(.required, for: .vertical)
documentsLabel.setContentCompressionResistancePriority(.required, for: .vertical)
view.addSubview(backgroundImage)
view.addSubview(willkommenLabel)
view.addSubview(textLabel)
view.addSubview(emailButton)
emailButton.addSubview(emailImage)
view.addSubview(oderLabel)
view.addSubview(lineLeft)
view.addSubview(lineRight)
view.addSubview(facebookButton)
facebookButton.addSubview(facebookLogo)
view.addSubview(googleButton)
googleButton.addSubview(googleLogo)
view.addSubview(appleButton)
appleButton.addSubview(appleLogo)
view.addSubview(documentsLabel)
backgroundImage.topAnchor.constraint(equalTo: view.topAnchor, constant: -20).isActive = true
backgroundImage.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 20).isActive = true
backgroundImage.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: -20).isActive = true
backgroundImage.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 20).isActive = true
// add a layout guide for percentage top spacing
let topSpaceGuide = UILayoutGuide()
view.addLayoutGuide(topSpaceGuide)
// based on iPhone 8 ... 80-pts from top
// will be shorter on smaller devices, taller on larger devices
topSpaceGuide.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
topSpaceGuide.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 80.0 / 667.0).isActive = true
willkommenLabel.topAnchor.constraint(equalTo: topSpaceGuide.bottomAnchor).isActive = true
willkommenLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
willkommenLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
// textLabel top constrained to willkommenLabel bottom
textLabel.topAnchor.constraint(equalTo: willkommenLabel.bottomAnchor, constant: 0).isActive = true
textLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
textLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
// textLabel height = a percentage of view height using 100-pts based on an iPhone 8
// priority = .defaultHigh so it can be compressed if needed (on smaller devices)
let c = textLabel.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 100.0 / 667.0)
c.priority = .defaultHigh
c.isActive = true
// set email button height
emailButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
// set other button heights equal to emailButton
facebookButton.heightAnchor.constraint(equalTo: emailButton.heightAnchor).isActive = true
googleButton.heightAnchor.constraint(equalTo: emailButton.heightAnchor).isActive = true
appleButton.heightAnchor.constraint(equalTo: emailButton.heightAnchor).isActive = true
// add the logo images to the buttons, and make their heights relative to button heights
// in case you want to change the button heights
for (btn, img) in [(emailButton, emailImage), (facebookButton, facebookLogo), (googleButton, googleLogo), (appleButton, appleLogo)] {
btn.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
btn.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
btn.addSubview(img)
img.centerYAnchor.constraint(equalTo: btn.centerYAnchor).isActive = true
img.leadingAnchor.constraint(equalTo: btn.leadingAnchor, constant: 10).isActive = true
img.heightAnchor.constraint(equalTo: btn.heightAnchor, multiplier: 0.5).isActive = true
img.widthAnchor.constraint(equalTo: img.heightAnchor).isActive = true
}
emailButton.topAnchor.constraint(equalTo: textLabel.bottomAnchor, constant: 20).isActive = true
oderLabel.topAnchor.constraint(equalTo: emailButton.bottomAnchor, constant: 15).isActive = true
facebookButton.topAnchor.constraint(equalTo: oderLabel.bottomAnchor, constant: 15).isActive = true
googleButton.topAnchor.constraint(equalTo: facebookButton.bottomAnchor, constant: 10).isActive = true
appleButton.topAnchor.constraint(equalTo: googleButton.bottomAnchor, constant: 10).isActive = true
// make sure appleButton stays above documentsLabel
appleButton.bottomAnchor.constraint(lessThanOrEqualTo: documentsLabel.topAnchor, constant: -20.0).isActive = true
// horizontal arrangement of oderLabel and left/right lines
oderLabel.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
oderLabel.widthAnchor.constraint(equalToConstant: 60).isActive = true
lineLeft.centerYAnchor.constraint(equalTo: oderLabel.centerYAnchor).isActive = true
lineLeft.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
lineLeft.trailingAnchor.constraint(equalTo: oderLabel.leadingAnchor).isActive = true
lineRight.centerYAnchor.constraint(equalTo: oderLabel.centerYAnchor).isActive = true
lineRight.leadingAnchor.constraint(equalTo: oderLabel.trailingAnchor).isActive = true
lineRight.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
// documentsLabel stay at bottom
documentsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true
documentsLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true
documentsLabel.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -5).isActive = true
}
Actually this depnds on how you wan the layout to look in every device , so if you want fixed height for all elements then you should wrap all elements inside a scrollview that will scroll for small devices and act if not exists in large devices , or if you need to make elements fit in screen in all devices then you should make height constraints proportional to screen height
If you want your design to work perfect in all devices then you have to avoid setting constant values as much as you can unless it's necessary, here you are setting heights and paddings fixed numbers, trying to set them related to screen size, for example you can here set all buttons in a view and set it's height to half of screen would be :
Let height = view.frame.size.height / 2
buttonView.heightAnchor.constrains(equalTo: height).isActive = true
And also insert the buttons inside a stackView covers the buttonsview and set it to fill equally for buttons. So you would have all buttons equally and not hard coded, as well as dynamic view of the buttons related ro screen size of whichever device runs the app

Swift - auto constrain items in ScrollView

I am having problems to constrain my items inside my UIScrollView, to be more specific trailing - anchors are behaving weird:
As you can see trailing-anchors are not the same as leading-anchors..
These are my constrains:
scrollView.topAnchor.constraint(equalTo: view.topAnchor, constant: 130).isActive = true
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 30).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -30).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
emailTextField.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
emailTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
emailTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
emailTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
anzeigeNameTextField.topAnchor.constraint(equalTo: emailTextField.topAnchor, constant: 80).isActive = true
anzeigeNameTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
anzeigeNameTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
anzeigeNameTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
wishlistHandleTextField.topAnchor.constraint(equalTo: anzeigeNameTextField.topAnchor, constant: 80).isActive = true
wishlistHandleTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
wishlistHandleTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
wishlistHandleTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
passwordTextField.topAnchor.constraint(equalTo: wishlistHandleTextField.topAnchor, constant: 80).isActive = true
passwordTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
passwordTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
passwordTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
eyeButtonOne.centerYAnchor.constraint(equalTo: passwordTextField.centerYAnchor, constant: 10).isActive = true
eyeButtonOne.trailingAnchor.constraint(equalTo: passwordTextField.trailingAnchor).isActive = true
passwordWiederholenTextField.topAnchor.constraint(equalTo: passwordTextField.topAnchor, constant: 80).isActive = true
passwordWiederholenTextField.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
passwordWiederholenTextField.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
passwordWiederholenTextField.heightAnchor.constraint(equalToConstant: 50).isActive = true
eyeButtonTwo.centerYAnchor.constraint(equalTo: passwordWiederholenTextField.centerYAnchor, constant: 10).isActive = true
eyeButtonTwo.trailingAnchor.constraint(equalTo: passwordWiederholenTextField.trailingAnchor).isActive = true
documentsLabel.topAnchor.constraint(equalTo: passwordWiederholenTextField.topAnchor, constant: 80).isActive = true
documentsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
documentsLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
documentsLabel.heightAnchor.constraint(equalToConstant: 50).isActive = true
signUpButton.topAnchor.constraint(equalTo: documentsLabel.topAnchor, constant: 80).isActive = true
signUpButton.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
signUpButton.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
signUpButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
What am I doing wrong? Do I have to constraint differently inside a UIScrollView? And if so, how and why?
create sample code for you. Hope it will be useful
and read this link for better sene below code: scrollView with auto layout
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.view.backgroundColor = UIColor.red.withAlphaComponent(0.5)
// create scrollView
let scrollView = UIScrollView.init()
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.backgroundColor = UIColor.blue.withAlphaComponent(0.5)
self.view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
// create tempView inside scrollView for use autolayout with scrollView
let tempView = UIView.init()
tempView.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(tempView)
tempView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
tempView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
tempView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
tempView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
// is important, just for use autolayout inside scrollView with scroll if content large screen
tempView.widthAnchor.constraint(equalTo: self.view.widthAnchor).isActive = true
let heightConstraint = tempView.heightAnchor.constraint(equalTo: self.view.heightAnchor)
heightConstraint.priority = .init(250)
heightConstraint.isActive = true
// create sample UI inside tempView
let emailTextField = UITextField.init()
emailTextField.translatesAutoresizingMaskIntoConstraints = false
emailTextField.backgroundColor = .white
tempView.addSubview(emailTextField)
emailTextField.topAnchor.constraint(equalTo: tempView.topAnchor, constant: 200).isActive = true
emailTextField.leadingAnchor.constraint(equalTo: tempView.leadingAnchor, constant: 50).isActive = true
emailTextField.trailingAnchor.constraint(equalTo: tempView.trailingAnchor, constant: -50).isActive = true
emailTextField.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
screenShot
uiview needed to be subview of scrollview, after that you can embed you're elements into uiview

How to add views to the UIScrolllView programmatically using anchors in Swift 4

I have some problems with UIScrollView. Trying to add scrollable are for some sliders but nothing is working. I think it is because I'm using anchors for layout. Please, explain to me, what I'm doing wrong. Eventually, I need to have a scrollable area for iPhone SE version that user can use sliders.
Here is my code, where I'm creating scrollview and adding all the sliders on it and putting anchors in relation with scrollview.
//MARK: Scroll View
scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: view.bounds.width, height: 812))
scrollView.backgroundColor = .gray
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
scrollView.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 0).isActive = true
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 0).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor, constant: 0).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0).isActive = true
scrollView.contentSize = CGSize(width: view.bounds.width, height: 812)
//MARK: Brightness Label
brightnessLabel = UILabel()
brightnessLabel.text = "Brightness"
brightnessLabel.font = UIFont(name: "Avenir", size: 14)
brightnessLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(brightnessLabel)
brightnessLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 10).isActive = true
brightnessLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
brightnessLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
brightnessLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Brightness Slider
brightnessSlider = UISlider()
brightnessSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
brightnessSlider.tintColor = .black
brightnessSlider.minimumValue = 0
brightnessSlider.maximumValue = 100
brightnessSlider.translatesAutoresizingMaskIntoConstraints = false
brightnessSlider.setValue(50, animated: true)
scrollView.addSubview(brightnessSlider)
brightnessSlider.topAnchor.constraint(equalTo: brightnessLabel.bottomAnchor, constant: 5).isActive = true
brightnessSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
brightnessSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
brightnessSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MArk: Brightness Value Label
brightnessValueLabel = UILabel()
brightnessValueLabel.text = "50"
brightnessValueLabel.font = UIFont(name: "Avenir", size: 14)
brightnessValueLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(brightnessValueLabel)
brightnessValueLabel.topAnchor.constraint(equalTo: collectionView.bottomAnchor, constant: 10).isActive = true
brightnessValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
brightnessValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: view.bounds.width - 30).isActive = true
brightnessValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Contrast Label
contrastLabel = UILabel()
contrastLabel.text = "Contrast"
contrastLabel.font = UIFont(name: "Avenir", size: 14)
contrastLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contrastLabel)
contrastLabel.topAnchor.constraint(equalTo: brightnessSlider.bottomAnchor, constant: 10).isActive = true
contrastLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
contrastLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
contrastLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Contrast Slider
contrastSlider = UISlider()
contrastSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
contrastSlider.tintColor = .black
contrastSlider.minimumValue = 0
contrastSlider.maximumValue = 100
contrastSlider.translatesAutoresizingMaskIntoConstraints = false
contrastSlider.setValue(50, animated: true)
scrollView.addSubview(contrastSlider)
contrastSlider.topAnchor.constraint(equalTo: contrastLabel.bottomAnchor, constant: 5).isActive = true
contrastSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
contrastSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
contrastSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MArk: Contrast Value Label
contrastValueLabel = UILabel()
contrastValueLabel.text = "50"
contrastValueLabel.font = UIFont(name: "Avenir", size: 14)
contrastValueLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(contrastValueLabel)
contrastValueLabel.topAnchor.constraint(equalTo: brightnessSlider.bottomAnchor, constant: 10).isActive = true
contrastValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
contrastValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: view.bounds.width - 30).isActive = true
contrastValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Saturation Label
saturationLabel = UILabel()
saturationLabel.text = "Saturation"
saturationLabel.font = UIFont(name: "Avenir", size: 14)
saturationLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(saturationLabel)
saturationLabel.topAnchor.constraint(equalTo: contrastSlider.bottomAnchor, constant: 10).isActive = true
saturationLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
saturationLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
saturationLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Saturation Slider
saturationSlider = UISlider()
saturationSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
saturationSlider.tintColor = .black
saturationSlider.minimumValue = 0
saturationSlider.maximumValue = 100
saturationSlider.translatesAutoresizingMaskIntoConstraints = false
saturationSlider.setValue(50, animated: true)
scrollView.addSubview(saturationSlider)
saturationSlider.topAnchor.constraint(equalTo: saturationLabel.bottomAnchor, constant: 5).isActive = true
saturationSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
saturationSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
saturationSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MArk: Saturation Value Label
saturationValueLabel = UILabel()
saturationValueLabel.text = "50"
saturationValueLabel.font = UIFont(name: "Avenir", size: 14)
saturationValueLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(saturationValueLabel)
saturationValueLabel.topAnchor.constraint(equalTo: contrastSlider.bottomAnchor, constant: 10).isActive = true
saturationValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
saturationValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: view.bounds.width - 30).isActive = true
saturationValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Noise Label
noiseLabel = UILabel()
noiseLabel.text = "Noise"
noiseLabel.font = UIFont(name: "Avenir", size: 14)
noiseLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(noiseLabel)
noiseLabel.topAnchor.constraint(equalTo: saturationSlider.bottomAnchor, constant: 10).isActive = true
noiseLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: view.bounds.width / -2).isActive = true
noiseLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
noiseLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MARK: Noise Slider
noiseSlider = UISlider()
noiseSlider.setThumbImage(UIImage(named: "sliderThumb"), for: .normal)
noiseSlider.tintColor = .black
noiseSlider.minimumValue = 0
noiseSlider.maximumValue = 100
noiseSlider.translatesAutoresizingMaskIntoConstraints = false
noiseSlider.setValue(50, animated: true)
scrollView.addSubview(noiseSlider)
noiseSlider.topAnchor.constraint(equalTo: noiseLabel.bottomAnchor, constant: 5).isActive = true
noiseSlider.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
noiseSlider.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 10).isActive = true
noiseSlider.heightAnchor.constraint(equalToConstant: 25).isActive = true
//MArk: Noise Value Label
noiseValueLabel = UILabel()
noiseValueLabel.text = "50"
noiseValueLabel.font = UIFont(name: "Avenir", size: 14)
noiseValueLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(noiseValueLabel)
noiseValueLabel.topAnchor.constraint(equalTo: saturationSlider.bottomAnchor, constant: 10).isActive = true
noiseValueLabel.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor, constant: -10).isActive = true
noiseValueLabel.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: view.bounds.width - 30).isActive = true
noiseValueLabel.heightAnchor.constraint(equalToConstant: 25).isActive = true
If somebody wonders, this is my view hierarchy, where all the items I need to be on scroll view are in fact on scroll view. I added gray color to scroll view to make it visable.
When you are using autolayout you do not need to specify contentSize, it will infer that from the subviews. The conditions are:
topmostView.topAnchor should be attached to scrollView.topAnchor
bottommostView.bottomAnchor should be attached to scrollView.bottomAnchor
Leading and trailing of scrollView should be attached to any view which has a width.
A few problems i see in your code.
You seem to have attached a label's leading and trailing to a scrollView which is not what you want to do most of the time because the scrollView width will be only as much as that of the label.
You also seem to have attached the scrollView leading and trailing at multiple places which will compute different leading and trailing constraints leading to conflicts. (I'm pretty sure if you check your log it will show some constraint breaking)
You have not attached the top or bottom of the topmost and bottommost views that are inside the scrollView to the scrollView's top and bottom.
Apple recommends that when using UIScrollView, have a dummy view (contentView) which will pinned to the top, leading, trailing, bottom of the scrollView and have a specified width. The height of that view will be calculated dynamically from the views you add to it. The same concept of the top of the topmost view attached to the contentView's top and bottom of the bottommost view attached to the contentView's bottom.
You can check my github repo where there is a minimalistic example which explains a UIScrollView with autolayout.

UIScrollView not able to scroll?

I have this UIScrollView set up with a UIImageView and a UILabel.
However, it's not scrolling like I want it to. It's like it's not even there... How can I fix this? I just want it to be scrollable (even though theres nothing at the bottom to scroll to yet)
Thx in advance!
Here's my code:
// scrollView
scrollView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(scrollView)
// scrollView constraints
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
// Initialize profileImageView
profileImageView.image = #imageLiteral(resourceName: "neil")
profileImageView.translatesAutoresizingMaskIntoConstraints = false
profileImageView.layer.cornerRadius = 125 / 2
profileImageView.clipsToBounds = true
scrollView.addSubview(profileImageView)
// Add profileImageView constraints
profileImageView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 45).isActive = true
profileImageView.heightAnchor.constraint(equalToConstant: 125).isActive = true
profileImageView.widthAnchor.constraint(equalToConstant: 125).isActive = true
profileImageView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
// Add separator view
seperator.translatesAutoresizingMaskIntoConstraints = false
seperator.backgroundColor = UIColor.darkGray
scrollView.addSubview(seperator)
// seperator constraints
seperator.heightAnchor.constraint(equalToConstant: 2).isActive = true
seperator.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 0.8).isActive = true
seperator.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
seperator.topAnchor.constraint(equalTo: profileImageView.bottomAnchor, constant: 20).isActive = true
//nameLabel
let nameLabelFont = UIFont.monospacedDigitSystemFont(ofSize: 36, weight: UIFont.Weight.heavy)
nameLabel.font = nameLabelFont
nameLabel.text = currentUser.name
nameLabel.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(nameLabel)
// nameLabel constraints
nameLabel.topAnchor.constraint(equalTo: seperator.bottomAnchor, constant: 10).isActive = true
nameLabel.leadingAnchor.constraint(equalTo: seperator.leadingAnchor).isActive = true
You can't scroll because scrollView.contentSize isn't bigger then your view.
If you want make your screen scrollable you should set scrollView.contentSize.
Put scrollView.contentSize = CGSize(width: 1000, height: 1000) before view.addSubview(scrollView) to set your scrollable area to 1000x1000.
Generally scrollView.contentSize should be calculated base on UI elements inside UIScrollView e.g. size of 10 images with space between them.

UIScrollView nested subviews not displayed using Auto Layout

I am trying to generate views displayed in a UIScrollView based on structured (XML) data at runtime. I generate all the views I need and add them to my scrollview afterwards, using Auto Layout to let the scrollview calculate its contentSize. Everything worked fine as long as I was only using UILabels and a custom UIImageView subclass, but now I want to add a nested view containing another view and a label to represent text with a vertical line on the left side.
The general layout code I'm using to add all generated views to the scrollview is as follows:
var previousAnchor = scrollView.topAnchor
views.forEach { (view) in
scrollView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
view.topAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
view.leadingAnchor.constraint(equalTo: self.view.leadingAnchor,
constant: UI.smallPadding).isActive = true
view.trailingAnchor.constraint(equalTo: self.view.trailingAnchor,
constant: -UI.smallPadding).isActive = true
previousAnchor = view.bottomAnchor
}
scrollView.bottomAnchor.constraint(equalTo: previousAnchor,
constant: UI.defaultPadding).isActive = true
And the layout code for my nested subview looks like this (it's a UIView subclass):
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor).isActive = true
line.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
line.widthAnchor.constraint(equalToConstant: 2).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.topAnchor.constraint(equalTo: topAnchor).isActive = true
label.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
If I use this code, I cannot see this view in my scrollview at all and the scrollview has a warning of ambiguous content size attached to it in the Debug View Hierarchy view. This is somewhat irritating, as using labels without being nested in a view like this does work like a charm. So I started playing around and what really puzzles me is that if I use the following code I can see the view itself (as I gave it a gray background color), but still can't see line and/or label, as both seem have a frame of (0,0,0,0) upon inspection:
addSubview(label)
addSubview(line)
line.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
line.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
line.widthAnchor.constraint(equalToConstant: 20).isActive = true
line.heightAnchor.constraint(equalToConstant: 20).isActive = true
line.trailingAnchor.constraint(equalTo: label.leadingAnchor).isActive = true
line.backgroundColor = UIColor.green
label.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
label.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
heightAnchor.constraint(equalToConstant: 100).isActive = true
heightAnchor.constraint(equalTo: label.heightAnchor).isActive = true
label.numberOfLines = 0
label.text = "Here's to the crazy ones. The misfits. The rebels. The troublemakers."
What am I missing? I also tried putting all my views into a container view instead of using them as subviews of the scrollview directly, but this did not help with my nested view and brought up other problems.

Resources