Vertical ScrollView not scrolling (No Storyboard) - ios

I need help creating a Scroll View without Storyboards. Here is my code for setting up the Scroll View; I'm not setting a contentSize of the Scroll View because I'd like the scroll view content size to be dynamic, dependent on the amount of text in the TextView. What I did instead, is I tried adding a 'contentView' to the Scroll View and added all my UI elements into the contentView. Any help would be appreciated.
import Foundation
import UIKit
import UITextView_Placeholder
class ComposerVC: UIViewController {
private var scrollView: UIScrollView = {
let scrollView = UIScrollView(frame: UIScreen.main.bounds)
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
return content
}()
private var title: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.placeholder = "Untitled"
title.textColor = UIColor(hexString: "#50E3C2")
title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
private var divider: UIView = {
let divider = UIView()
divider.translatesAutoresizingMaskIntoConstraints = false
divider.backgroundColor = UIColor(hexString: "#50E3C2")
return divider
}()
private var content: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
title.placeholder = "Begin writing here..."
title.textColor = .white
title.font = UIFont(name: "Avenir-Book", size: 15)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
override func viewDidLoad() {
setupUI()
setupUIConstraints()
title.delegate = self
}
private func setupUI() {
view.backgroundColor = UIColor(hexString: "#131415")
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(title)
contentView.addSubview(divider)
contentView.addSubview(content)
}
private func setupUIConstraints() {
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
title.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
title.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
title.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
divider.topAnchor.constraint(equalTo: title.bottomAnchor, constant: 15).isActive = true
divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
content.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
content.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
content.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
}
}
extension ComposerVC: UITextViewDelegate {
func textViewDidChange(_ textView: UITextView) {
let fixedWidth = textView.frame.size.width
let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
}
}

A couple tips:
Don't use existing names for variables... with your code as-is, private var title: UITextView causes problems (title is already a view controller property).
Use var names that imply the object... e.g. titleTextView and contentTextView instead of title and content
During development - particularly when you're working on layout - give your UI elements contrasting background colors so you can easily see their frames at runtime.
When using code-created views, set .clipsToBounds = true ... if you don't see any subviews you've added, you know the frame / constraints are missing something.
I don't have your UITextView_Placeholder import, but that shouldn't affect anything here...
So, first, change your viewDidLoad() to this:
override func viewDidLoad() {
setupUI()
setupUIConstraints()
titleTextView.delegate = self
// contrasting colors during development
scrollView.backgroundColor = .red
titleTextView.backgroundColor = .yellow
contentTextView.backgroundColor = .green
divider.backgroundColor = .blue
contentView.backgroundColor = .cyan
}
When you run it, you should see (scroll view background is red, and this is without the placeholder stuff):
It looks correct, except there's no cyan-colored contentView.
Now, clip the subviews of contentView:
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
// add this line
content.clipsToBounds = true
return content
}()
The result:
Where did everything go? Well, we didn't see the cyan contentView and now we don't see any of its subviews ... If we use Debug View Hierarchy we can find out contentView has a Height of Zero.
Fix that by constraining the bottom of the second text view in setupUIConstraints() (I've renamed it to contentTextView instead of just content):
contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true
and we get:
Now the Height of the cyan contentView is controlled by correctly setup constraints of its subviews.
As a side note: with constraints setup properly, and scrolling disabled for the text views, you do not need your:
extension ComposerVC: UITextViewDelegate {
//func textViewDidChange(_ textView: UITextView) {
//...
//}
}
The text view will automatically size itself to its text:
Here's the complete edited code:
class ComposerVC: UIViewController {
private var scrollView: UIScrollView = {
let scrollView = UIScrollView(frame: UIScreen.main.bounds)
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
private var contentView: UIView = {
let content = UIView()
content.translatesAutoresizingMaskIntoConstraints = false
// add this line so we know if the constraints are set correctly
content.clipsToBounds = true
return content
}()
private var titleTextView: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
// title.placeholder = "Untitled"
title.textColor = UIColor(hexString: "#50E3C2")
title.font = UIFont(name: "Rubik-BoldItalic", size: 32)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
private var divider: UIView = {
let divider = UIView()
divider.translatesAutoresizingMaskIntoConstraints = false
divider.backgroundColor = UIColor(hexString: "#50E3C2")
return divider
}()
private var contentTextView: UITextView = {
let title = UITextView()
title.translatesAutoresizingMaskIntoConstraints = false
// title.placeholder = "Begin writing here..."
title.textColor = .white
title.font = UIFont(name: "Avenir-Book", size: 15)
title.backgroundColor = .clear
title.isScrollEnabled = false
return title
}()
override func viewDidLoad() {
setupUI()
setupUIConstraints()
titleTextView.delegate = self
// contrasting colors during development
scrollView.backgroundColor = .red
titleTextView.backgroundColor = .yellow
contentTextView.backgroundColor = .green
divider.backgroundColor = .blue
contentView.backgroundColor = .cyan
}
private func setupUI() {
view.backgroundColor = UIColor(hexString: "#131415")
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(titleTextView)
contentView.addSubview(divider)
contentView.addSubview(contentTextView)
}
private func setupUIConstraints() {
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
contentView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
contentView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
contentView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
contentView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
titleTextView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 95).isActive = true
titleTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
titleTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
divider.topAnchor.constraint(equalTo: titleTextView.bottomAnchor, constant: 15).isActive = true
divider.centerXAnchor.constraint(equalTo: contentView.centerXAnchor).isActive = true
divider.heightAnchor.constraint(equalToConstant: 1).isActive = true
divider.widthAnchor.constraint(equalTo: contentView.widthAnchor, multiplier: 0.8).isActive = true
contentTextView.topAnchor.constraint(equalTo: divider.bottomAnchor, constant: 15).isActive = true
contentTextView.leftAnchor.constraint(equalTo: contentView.leftAnchor, constant: 35).isActive = true
contentTextView.rightAnchor.constraint(equalTo: contentView.rightAnchor, constant: -35).isActive = true
contentTextView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -95).isActive = true
}
}
extension ComposerVC: UITextViewDelegate {
// func textViewDidChange(_ textView: UITextView) {
// let fixedWidth = textView.frame.size.width
// let newSize = textView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
// textView.frame.size = CGSize(width: max(newSize.width, fixedWidth), height: newSize.height)
// }
}

Assuming you are using iOS 11+, Your contentView should have its anchors constrained to the contentLayoutGuide of the scrollView. like this:
contentView
.leadingAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.leadingAnchor).isActive = true
contentView
.topAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.topAnchor).isActive = true
contentView
.trailingAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.trailingAnchor).isActive = true
contentView
.bottomAnchor
.constraint(equalTo: scrollView.contentLayoutGuide.bottomAnchor).isActive = true
Also, its width should be constrained to the scrollView's frameLayoutGuide and not the view's width, like this:
contentView
.widthAnchor
.constraint(equalTo: scrollView.frameLayoutGuide.widthAnchor).isActive = true
That should make the scrollView detect the content size to fit.

Related

Setting UITextField heightAnchor to 0 does not work on iOS 15

I have an iOS application where i have a textfield and a button and on tap of button i have to hide the textfield.
I am setting heightAnchor to 0 on tap of button. Everything is working fine on iOS 14(14.5) but does not work(does not hide the text field) on iOS 15. Also, I have tried setting up the isHidden property on UITextField but it does not work.
Can you please help tell if something changed or i am doing something wrong. Thank you.
Code reference:
import UIKit
class ViewController: UIViewController {
private lazy var mytextFeild: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
var heightConstraint: NSLayoutConstraint?
#objc func buttonTapped() {
heightConstraint?.isActive = false
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 0)
heightConstraint?.isActive = true
}
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mytextFeild)
view.addSubview(testView)
view.addSubview(button)
mytextFeild.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32).isActive = true
mytextFeild.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
mytextFeild.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
heightConstraint = mytextFeild.heightAnchor.constraint(equalToConstant: 32.0)
heightConstraint?.isActive = true
button.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 32.0).isActive = true
button.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -32.0).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -64.0).isActive = true
testView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
testView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
testView.topAnchor.constraint(equalTo: mytextFeild.bottomAnchor).isActive = true
testView.bottomAnchor.constraint(equalTo: button.topAnchor).isActive = true
}
}
Add them to a stack then add stack to the viewController. at the end try to hide it easily without changing the height.
class ViewController: UIViewController {
private lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .fill
stackView.alignment = .center
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
private lazy var myTextField: UITextField = {
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.text = "Hello world"
textField.backgroundColor = .green
return textField
}()
private lazy var testView: UIView = {
let view = UIView(frame: .zero)
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = .systemPink
return view
}()
private lazy var button: UIButton = {
let view = UIButton()
view.backgroundColor = .blue
view.setTitle("hide it", for: .normal)
view.translatesAutoresizingMaskIntoConstraints = false
view.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
return view
}()
#objc func buttonTapped() {
myTextField.isHidden = !myTextField.isHidden
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
stackView.addArrangedSubview(myTextField)
stackView.addArrangedSubview(testView)
stackView.addArrangedSubview(button)
view.addSubview(stackView)
stackView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -64.0).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
myTextField.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
myTextField.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
testView.widthAnchor.constraint(equalTo: view.widthAnchor).isActive = true
button.heightAnchor.constraint(equalToConstant: 32.0).isActive = true
button.widthAnchor.constraint(equalTo: view.widthAnchor, constant: -64.0).isActive = true
}
}

How do I add a UIView to a UIScrollView In Swift programmatically?

I am trying to add a view to a UIScrollView just using code, but the view doesn't appear in the UIScrollView and I'm not sure why. When I added a button or label, they show up.
import UIKit
class profileViewController: UIViewController, UIScrollViewDelegate {
var label : UILabel = {
let label = UILabel()
label.text = "Profile"
label.textColor = UIColor.init(white: 0.80, alpha: 1)
label.font = UIFont.systemFont(ofSize: 40)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var scrollview : UIScrollView = {
let scrollview = UIScrollView()
scrollview.translatesAutoresizingMaskIntoConstraints = false
scrollview.backgroundColor = .clear
return scrollview
}()
var greyview : UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.init(white: 0.70, alpha: 1)
view.backgroundColor = UIColor.gray
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(label)
label.centerYAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerYAnchor).isActive = true
label.centerXAnchor.constraint(equalTo: view.safeAreaLayoutGuide.centerXAnchor).isActive = true
scrollview.delegate = self
view.addSubview(scrollview)
scrollview.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
scrollview.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
scrollview.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
scrollview.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
scrollview.contentSize = CGSize.init(width: view.frame.size.width, height: view.frame.size.height + 500)
scrollview.addSubview(greyview)
greyview.topAnchor.constraint(equalTo: scrollview.topAnchor).isActive = true
greyview.trailingAnchor.constraint(equalTo: scrollview.trailingAnchor).isActive = true
greyview.leadingAnchor.constraint(equalTo: scrollview.leadingAnchor).isActive = true
greyview.heightAnchor.constraint(equalToConstant: 100).isActive = true
}
}
this is one way:
self.scrollView = UIScrollView.init()
if let scrollView = self.scrollView {
scrollView.showsVerticalScrollIndicator = true
self.view.add(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
self.addConstraint(UtilConstraint.addTopConstraint(from: scrollView, to: self, value: 0))
self.addConstraint(UtilConstraint.addBottomConstraint(from: scrollView, to: self, value: 0))
self.addConstraint(UtilConstraint.addLeftLeftConstraint(from: scrollView, to: self, value: 0))
self.addConstraint(UtilConstraint.addRightRightConstraint(from: scrollView, to: self, value: 0))
NSLayoutConstraint.activate(self.constraints)
greyview.translatesAutoresizingMaskIntoConstraints = false
scrollView.addSubview(greyview)
greyview.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
greyview.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
greyview.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
greyview.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
}
Remember, your greyview should have hight defined either statically or via. components inside.
though , what you were missing was defining a width. I have done it using widthAnchor. (assuming you need a vertical scroll)
This is probably because your greyview doesnt have its bottomAnchor. It needs the top and bottom anchors in order to work properly inside the scrollView:
scrollview.addSubview(greyview)
greyview.topAnchor.constraint(equalTo: scrollview.topAnchor).isActive = true
greyview.centerXAnchor.constraint(equalTo: scrollview.centerXAnchor).isActive = true
greyview.heightAnchor.constraint(equalToConstant: 100).isActive = true
greyview.widthAnchor.constraint(equalToConstant: 100).isActive = true
Add a widthAnchor, here I centered it in the scroll view but it is up to you to place it how you want it. Also if you add more items just make sure the bottom-most item has a bottomAnchor attached to the scrollView bottomAnchor or it will not scroll.
Update:
I don't know how you want your greyview to look, but if you make the height taller than the contentSize of the scrollView it will scroll, and make sure you have the bottomAnchor:
scrollview.contentSize = CGSize.init(width: view.frame.size.width, height: view.frame.size.height)
scrollview.addSubview(greyview)
greyview.topAnchor.constraint(equalTo: scrollview.topAnchor).isActive = true
greyview.bottomAnchor.constraint(equalTo: scrollview.bottomAnchor).isActive = true
greyview.heightAnchor.constraint(equalTo: scrollview.heightAnchor, constant: 500).isActive = true
greyview.widthAnchor.constraint(equalTo: scrollview.widthAnchor).isActive = true
This makes the greyview width equal to scrollview width, and height equal to scrollview height + 500 so that it scrolls.

Swift textview growing direction

so i have a textview inside a uiview. And my question is how to make my textivew grow in up direction when textview goes to the next line as well as my uiview.
var textheightcontraint : NSLayoutConstraint!
var viewheightconstraint : NSLayoutConstraint!
func setup4(){
view.addSubview(colorview)
colorview.topAnchor.constraint(equalTo: customtableview.bottomAnchor).isActive = true
colorview.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
colorview.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
viewheightconstraint = colorview.heightAnchor.constraint(equalToConstant: 44)
viewheightconstraint.isActive = true
colorview.backgroundColor = UIColor.lightGray
colorview.addSubview(customtextview2)
customtextview2.backgroundColor = .white
customtextview2.leftAnchor.constraint(equalTo: colorview.leftAnchor).isActive = true
customtextview2.rightAnchor.constraint(equalTo: colorview.rightAnchor, constant: -20).isActive = true
customtextview2.bottomAnchor.constraint(equalTo: colorview.bottomAnchor).isActive = true
textheightcontraint = customtextview2.heightAnchor.constraint(equalToConstant: 39)
textheightcontraint.isActive = true
customtextview2.delegate = self
}
func setuptextview(){
let fixedWidth = customtextview2.frame.size.width
let newSize = customtextview2.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))
self.textheightcontraint.constant = newSize.height
self.viewheightconstraint.constant = newSize.height
self.view.layoutIfNeeded()
}
func textViewDidChange(_ textView: UITextView) {
setuptextview()
}
If I understand your question, you want the text view (and the view it's contained in) to keep its bottom at the same position, and expand upward as you type?
To do that, disable scrolling in the text view, and set up your constraints so the bottom of the containing view is constrained to a y-position:
//
// ViewController.swift
//
// Created by Don Mag on 8/9/18.
//
import UIKit
class ViewController: UIViewController {
var theContainingView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
var theTextView: UITextView = {
let v = UITextView()
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .yellow
theContainingView.backgroundColor = .cyan
theContainingView.addSubview(theTextView)
view.addSubview(theContainingView)
NSLayoutConstraint.activate([
theTextView.topAnchor.constraint(equalTo: theContainingView.topAnchor, constant: 8.0),
theTextView.bottomAnchor.constraint(equalTo: theContainingView.bottomAnchor, constant: -8.0),
theTextView.leadingAnchor.constraint(equalTo: theContainingView.leadingAnchor, constant: 8.0),
theTextView.trailingAnchor.constraint(equalTo: theContainingView.trailingAnchor, constant: -8.0),
theContainingView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 40.0),
theContainingView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -40.0),
// Constrain the Bottom of the containing view. As the textView grows (or shrinks) with input,
// the TOP of the view will move up or down
theContainingView.bottomAnchor.constraint(equalTo: view.topAnchor, constant: 300.0),
])
theTextView.isScrollEnabled = false
theTextView.text = "This is the starting text."
}
}
Results:

Text jumps while automatically resize textView in swift

if have a simple messageView in my app. on the messageView is a input-container with a textView. The textView should resize depending on its content.
It works so far, but every time while wrapping into the next line the text "jumps" for the first character, but reposition with the second character. it looks like:
the most of my code. i assume that it has something to do with the scroll capabilities of the textView(?)
private let container: UIView = {
let view = UIView()
view.backgroundColor = UIColor.white
view.layer.cornerRadius = 20
view.layer.masksToBounds = true
view.layer.borderColor = UIColor(red:0.90, green:0.90, blue:0.90, alpha:1.0).cgColor
view.layer.borderWidth = 0.5
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private lazy var inputTV: UITextView = {
let tv = UITextView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont(name: "OpenSans-Light", size: 16)
tv.backgroundColor = .red
tv.delegate = self
tv.textContainer.lineBreakMode = .byWordWrapping
return tv
}()
override internal init(frame: CGRect) {
super.init(frame: CGRect.zero)
translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
container.addSubview(inputTV)
container.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -16).isActive = true
container.leftAnchor.constraint(equalTo: leftAnchor , constant: 16).isActive = true
container.rightAnchor.constraint(equalTo: rightAnchor, constant: -16).isActive = true
containerHeightAnchor = container.heightAnchor.constraint(equalToConstant: 40)
containerHeightAnchor?.isActive = true
inputTV.leftAnchor.constraint(equalTo: uploadButton.rightAnchor).isActive = true
inputTV.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
inputTV.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -5).isActive = true
textViewHeightAnchor = inputTV.heightAnchor.constraint(equalTo: container.heightAnchor)
textViewHeightAnchor?.isActive = true
}
internal func textViewDidChange(_ textView: UITextView) {
let contentHeight = textView.contentSize.height
containerHeightAnchor?.constant = max(contentHeight, 40)
inputTV.frame.size.height = contentHeight
}
I hope somebody could help. regards
thanks to #DonMag i do remove the heightAnchor from textViewand it does work for me. The new code is as following:
disable scrolling on the textView (isScrollEnabled = false)
private lazy var inputTV: UITextView = {
let tv = UITextView()
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont(name: "OpenSans-Light", size: 16)
tv.delegate = self
tv.isScrollEnabled = false
return tv
}()
remove heightAnchor (no inputTV.heightAnchor resp. my textViewHeightAnchor)
override internal init(frame: CGRect) {
super.init(frame: CGRect.zero)
translatesAutoresizingMaskIntoConstraints = false
addSubview(container)
container.addSubview(inputTV)
...
inputTV.leftAnchor.constraint(equalTo: uploadButton.rightAnchor).isActive = true
inputTV.centerYAnchor.constraint(equalTo: container.centerYAnchor).isActive = true
inputTV.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -5).isActive = true
}

Programmatically layed out UIScrollView, and added auto layout to it's subviews, but it does not scroll

I am trying to figure out how UIScrollView works and I added some subviews to it with different backgroundColor properties. I layed out the subviews with ios9 autolayout but even if the views are outside of the screen, the UIScrollView still does not scroll.
import UIKit
class ViewController: UIViewController {
let scrollView: UIScrollView = {
let sv = UIScrollView()
sv.translatesAutoresizingMaskIntoConstraints = false
sv.backgroundColor = .gray
return sv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(scrollView)
scrollView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
scrollView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
let view1 = UIView()
view1.backgroundColor = .red
let view2 = UIView()
view2.backgroundColor = .blue
let view3 = UIView()
view3.backgroundColor = .green
let view4 = UIView()
view4.backgroundColor = .purple
let views = [view1, view2, view3, view4]
for view in views {
scrollView.addSubview(view)
view.translatesAutoresizingMaskIntoConstraints = false
}
view1.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
view1.leftAnchor.constraint(equalTo: scrollView.leftAnchor).isActive = true
view1.heightAnchor.constraint(equalToConstant: 140).isActive = true
view1.widthAnchor.constraint(equalToConstant: 140).isActive = true
view2.topAnchor.constraint(equalTo: view1.bottomAnchor, constant: 100).isActive = true
view2.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view2.heightAnchor.constraint(equalToConstant: 140).isActive = true
view2.widthAnchor.constraint(equalToConstant: 140).isActive = true
view3.topAnchor.constraint(equalTo: view2.bottomAnchor, constant: 50).isActive = true
view3.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view3.heightAnchor.constraint(equalToConstant: 140).isActive = true
view3.widthAnchor.constraint(equalToConstant: 140).isActive = true
view4.topAnchor.constraint(equalTo: view3.bottomAnchor, constant: 20).isActive = true
view4.leftAnchor.constraint(equalTo: view1.rightAnchor).isActive = true
view4.heightAnchor.constraint(equalToConstant: 140).isActive = true
view4.widthAnchor.constraint(equalToConstant: 140).isActive = true
}
}
When using Autolayout in UIScrollViews you have to pin subviews both to the top and bottom of the scrollview which allows the scrollview to calculate its contentSize.
Adding this line fixes it:
view4.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0).isActive = true

Resources