Swift: How to resize the font size within a UITextView while using AutoLayout? - ios

I am one week into Swift programing and I want to build my first Application with Autolayout.
The current state of my app is that I generate a bunch of PictureCell in my ViewController. Their size is based on a slider value (and also calculated in the ViewController). This works just fine.
My struggle is customizing the inside of my PictureCell. My goal is to have a Label in the cell which font size is automatically resized when I resize the cell.
At the current state I can resize the Cell and the UITextView like I want, but I cannot resize the font within the Textview because it's constant is just called when it is initialized (I guess).
How can I address this problem in a good way?
Due to a not understanding of Swifts logic I have to post the whole code of the PictureCell:
class PictureCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.layer.cornerRadius = self.bounds.width / 20
self.clipsToBounds = true
setupViews()
}
let descriptionTextView: UITextView = {
let textView = UITextView()
textView.text = "Header"
textView.textColor = .black
textView.backgroundColor = .white
textView.translatesAutoresizingMaskIntoConstraints = false
textView.textAlignment = .center
textView.isEditable = false
textView.isScrollEnabled = false
textView.sizeToFit()
textView.font = UIFont.boldSystemFont(ofSize: textView.contentSize.height / 2) // Resize that
textView.layer.borderWidth = 2
textView.layer.borderColor = UIColor.red.cgColor
return textView
}()
var mainPicture: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.clipsToBounds = true
return imageView
}()
func setPictureForIndex(index: Int) {
self.mainPicture.image = UIImage(named: "color\(index)")
}
func setupViews() {
addSubview(mainPicture)
confMainPicture()
addSubview(descriptionTextView)
confDescriptionTextView()
}
func confMainPicture() {
mainPicture.translatesAutoresizingMaskIntoConstraints = false
mainPicture.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
mainPicture.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
mainPicture.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainPicture.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
}
func confDescriptionTextView(){
descriptionTextView.translatesAutoresizingMaskIntoConstraints = false
descriptionTextView.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
descriptionTextView.heightAnchor.constraint(equalTo: mainPicture.heightAnchor, multiplier: 0.25).isActive = true
descriptionTextView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
}
Too small for the text
Just fine
Too big to look good

This Code solved my problem more or less:
It doesn't work properly if the Cell is really small but it's better than the starting point and maybe someone can use it.
class PictureCell: UICollectionViewCell {
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
self.layer.cornerRadius = self.bounds.width / 20
self.clipsToBounds = true
setupViews()
}
//MARK: -
var cellIdetifier = Int()
var mainPicture: UIImageView = {
let imageView = UIImageView()
imageView.contentMode = .scaleAspectFill
imageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
imageView.clipsToBounds = true
imageView.layer.cornerRadius = imageView.bounds.width / 20
return imageView
}()
var descriptionBox: UIView = {
let descVie = UIView()
descVie.backgroundColor = UIColor(red: 0.1 , green: 0.1, blue: 0.1, alpha: 0.5)
descVie.layer.borderWidth = 0.5
descVie.layer.borderColor = UIColor.black.cgColor
descVie.clipsToBounds = true
descVie.layer.cornerRadius = descVie.bounds.height / 5
return descVie
}()
lazy var descLabel: UITextField = {
let label = UITextField()
label.textColor = .white
label.textAlignment = .center
label.clipsToBounds = true
label.font = UIFont.systemFont(ofSize: 15)
label.adjustsFontSizeToFitWidth = true
label.autoresizingMask = [.flexibleWidth,.flexibleHeight]
label.sizeToFit()
label.layoutIfNeeded()
label.isUserInteractionEnabled = false
return label
}()
func setPictureForIndex(index: Int, name: String) {
self.descLabel.text = name
self.mainPicture.image = UIImage(named: "color\(index)")
}
// MARK: -
// MARK: Layout
func setupViews() {
addSubview(mainPicture)
addSubview(descriptionBox)
descriptionBox.addSubview(descLabel)
confBounds()
}
func confBounds() {
mainPicture.translatesAutoresizingMaskIntoConstraints = false
mainPicture.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
mainPicture.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
mainPicture.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
mainPicture.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
descriptionBox.translatesAutoresizingMaskIntoConstraints = false
descriptionBox.widthAnchor.constraint(equalTo: self.widthAnchor).isActive = true
descriptionBox.heightAnchor.constraint(equalTo: mainPicture.heightAnchor, multiplier: 0.25).isActive = true
descriptionBox.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
descriptionBox.bottomAnchor.constraint(greaterThanOrEqualTo: mainPicture.topAnchor, constant: 1)
descLabel.translatesAutoresizingMaskIntoConstraints = false
descLabel.widthAnchor.constraint(equalTo: descriptionBox.widthAnchor).isActive = true
descLabel.heightAnchor.constraint(equalTo: descriptionBox.heightAnchor).isActive = true
descLabel.bottomAnchor.constraint(equalTo: descriptionBox.bottomAnchor).isActive = true
descLabel.heightAnchor.constraint(equalTo: descriptionBox.heightAnchor).isActive = true
}
}

Related

Trying to programmatically add UIScrollView to my App and I keep getting a blank View

Trying to create a UIScrollView and I cant seem to get my labels to appear as I would like them dead center.
lazy var label : UILabel = {
let label = UILabel()
label.text = "center of container view.center of container view"
label.font = UIFont(name: "Bebas Neue", size: 23)!
label.textColor = .black
return label
}()
// Mark: Properties
lazy var contentViewSize = CGSize(width: self.view.frame.width + 1200, height: self.view.frame.height)
// Mark: Views
fileprivate lazy var parentScrollView : UIView = {
var newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = .black
newView.frame = self.scrollView.bounds
newView.frame.size = contentViewSize
return newView
}()
fileprivate lazy var scrollView : UIScrollView = {
var uiScrollView = UIScrollView(frame: .zero)
uiScrollView.translatesAutoresizingMaskIntoConstraints = false
uiScrollView.frame = view.bounds
uiScrollView.backgroundColor = .gray
uiScrollView.addViewBorder(borderColor: UIColor.gray.cgColor, borderWith: 10, borderCornerRadius: 0)
// uiScrollView.alpha =
uiScrollView.contentSize = contentViewSize
// uiScrollView.autoresizingMask = .flexibleHeight
uiScrollView.showsVerticalScrollIndicator = true
uiScrollView.bounces = true
return uiScrollView
}()
fileprivate lazy var tutorialView : UIStackView = {
var tutView = UIStackView(arrangedSubviews: [label,label,label])
tutView.axis = .horizontal
tutView.backgroundColor = .white
tutView.distribution = .fillEqually
tutView.spacing = 0
return tutView
}()
override func viewDidLoad() {
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(parentScrollView)
parentScrollView.addSubview(tutorialView)
scrollView.widthAnchor.constraint(equalTo:self.view.widthAnchor,multiplier: 0.9).isActive = true
scrollView.heightAnchor.constraint(equalTo:self.view.heightAnchor,multiplier: 0.7).isActive = true
scrollView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 75).isActive = true
tutorialView.topAnchor.constraint(equalTo: parentScrollView.topAnchor,constant: +10).isActive = true}
It seems that when I add that last constraight with the topAnchor I get a blank screen. When I take that off I actually get the borders between the two views and I am able to do some scrolling.
Any help would be appreciated. Thank you
Did you give a value to the scrollView?
uiScrollView.contentSize.width = 1200
Then give the tutorialView the same width constraint with the other constraints.
NSLayoutConstraint.activate([
tutorialView.widthAnchor.constraint(equalToConstant: 1200),
tutorialView.heightAnchor.constraint(equalTo: uiScrollView.heightAnchor)
])
You have to add tutView.translatesAutoresizingMaskIntoConstraints = false when giving layout constraints in closure of tutorialView and also there are some mistakes in your code. So included some changes in your code and check updated code:
lazy var label : UILabel = {
let label = UILabel()
label.text = "center of container view.center of container view"
label.font = UIFont(name: "Bebas Neue", size: 23)!
label.textColor = .green
return label
}()
// Mark: Properties
lazy var contentViewSize = CGSize(width: self.view.frame.width + 1200, height: self.view.frame.height)
// Mark: Views
fileprivate lazy var parentScrollView : UIView = {
var newView = UIView()
newView.translatesAutoresizingMaskIntoConstraints = false
newView.backgroundColor = UIColor.clear
// newView.layer.borderWidth = 2
// newView.layer.borderColor = UIColor.yellow.cgColor
return newView
}()
fileprivate lazy var scrollView : UIScrollView = {
var uiScrollView = UIScrollView()
uiScrollView.translatesAutoresizingMaskIntoConstraints = false
// uiScrollView.frame = view.bounds
uiScrollView.backgroundColor = .orange
uiScrollView.layer.borderColor = UIColor.blue.cgColor
uiScrollView.layer.borderWidth = 2
uiScrollView.layer.cornerRadius = 0
// uiScrollView.alpha =
uiScrollView.contentSize = contentViewSize
// uiScrollView.autoresizingMask = .flexibleHeight
uiScrollView.showsVerticalScrollIndicator = true
uiScrollView.bounces = true
return uiScrollView
}()
fileprivate lazy var tutorialView : UIStackView = {
var tutView = UIStackView(arrangedSubviews: [label,label,label])
tutView.axis = .horizontal
tutView.backgroundColor = .red
tutView.distribution = .fill
tutView.translatesAutoresizingMaskIntoConstraints = false
return tutView
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(parentScrollView)
parentScrollView.addSubview(tutorialView)
scrollView.widthAnchor.constraint(equalTo:self.view.widthAnchor,multiplier: 0.9).isActive = true
scrollView.heightAnchor.constraint(equalTo:self.view.heightAnchor,multiplier: 0.7).isActive = true
scrollView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor).isActive = true
scrollView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 25).isActive = true
parentScrollView.centerXAnchor.constraint(equalTo: scrollView.centerXAnchor).isActive = true
parentScrollView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 10).isActive = true
parentScrollView.heightAnchor.constraint(equalToConstant: 200).isActive = true
parentScrollView.widthAnchor.constraint(equalToConstant: 200).isActive = true
// scrollView.layer.borderWidth = 2
// scrollView.layer.borderColor = UIColor.blue.cgColor
tutorialView.heightAnchor.constraint(equalTo: parentScrollView.heightAnchor).isActive = true
tutorialView.widthAnchor.constraint(equalToConstant: 1200).isActive = true
}

Add UILabel as subview of UITextField on top

I am in the process of implementing a UILabel as a subview of a UITextField which will be shown right above the UITextField itself. The UITextField has a rounded border and what I would like to achieve is the UILabel to be shown over the border.
Everything currently works as expected, but the UILabel is drawn behind the border of the UITextField. I want it to go "over" (above) the border so the white backgroundColor would be shown above part of the border and make the text more easily readible.
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
In my CustomTextField class (subclass of UITextField):
public var labelText: String?
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
func setupLabel() {
self.addSubview(topLabel)
topLabel.centerYAnchor.constraint(equalTo: self.topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 20).isActive = true
topLabel.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -20).isActive = true
topLabel.text = labelText
}
I call setupLabel() at the end of the draw(_ rect: CGRect) method of UITextField (because I work with this to show the EUR sign always behind the entered value).
I have tried to play around with bringSubviewToFront and changing the zPosition of the layer of the UILabel, without success.
It now looks like this:
How can I bring the text "above" the border on the top?
EDIT: Tried Sh_Khan's solution, but it's still hidden behind the border.
import Foundation
import UIKit
public class CustomTextView: UIView, UITextFieldDelegate {
public var labelText: String?
var customTextField: CustomTextField = {
let customTextField = CustomTextField()
customTextField.translatesAutoresizingMaskIntoConstraints = false
customTextField.font = UIFont.systemFont(ofSize: 15)
customTextField.textColor = .black
customTextField.textAlignment = .center
customTextField.text = "0"
customTextField.suffix = "EUR"
customTextField.suffixTextColor = .lightGray
customTextField.suffixSpacing = 2.0
return customTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .darkGray
topLabel.numberOfLines = 1
topLabel.backgroundColor = .red
topLabel.textAlignment = .center
return topLabel
}()
override public init(frame: CGRect) {
super.init(frame: frame)
setupBorders()
}
public override func layoutSubviews() {
setupViews()
}
func setupBorders() {
self.layer.cornerRadius = 10.0
self.layer.borderColor = UIColor.lightGray.cgColor
self.layer.borderWidth = 1.0
}
func setupViews() {
addSubview(topLabel)
// insertSubview(topLabel, aboveSubview: customTextField)
insertSubview(customTextField, belowSubview: topLabel)
customTextField.topAnchor.constraint(equalTo: topAnchor).isActive = true
customTextField.leadingAnchor.constraint(equalTo: leadingAnchor).isActive = true
customTextField.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
customTextField.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
topLabel.centerYAnchor.constraint(equalTo: topAnchor).isActive = true
topLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
topLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
topLabel.text = labelText
}
public required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupViews()
}
}
You can try to organize it by creating a UIView subclass , so everything appear properly in it's order of adding
class CustomView: UIView {
var priceTextField: CustomTextField = {
let priceTextField = CustomTextField()
priceTextField.layer.cornerRadius = 10.0
priceTextField.layer.borderWidth = 1.0
priceTextField.layer.borderColor = UIColor.darkGray.cgColor
priceTextField.translatesAutoresizingMaskIntoConstraints = false
priceTextField.font = UIFont.systemFont(ofSize: 15)
priceTextField.textColor = .black
priceTextField.text = "0"
priceTextField.suffix = "EUR"
priceTextField.suffixTextColor = .darkGray
priceTextField.suffixSpacing = 2.0
priceTextField.textAlignment = .center
priceTextField.labelText = "Price"
return priceTextField
}()
var topLabel: UILabel = {
let topLabel = UILabel()
topLabel.translatesAutoresizingMaskIntoConstraints = false
topLabel.textAlignment = .center
topLabel.font = UIFont.systemFont(ofSize: 12)
topLabel.textColor = .lightGray
topLabel.backgroundColor = .white
topLabel.numberOfLines = 1
return topLabel
}()
var lableStr:String?
init(frame: CGRect,lblTex:String) {
super.init(frame: frame)
lableStr = lblTex
createSubviews()
}
override init(frame: CGRect) {
super.init(frame: frame)
createSubviews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
createSubviews()
}
func createSubviews() {
// all the layout code from above
// add the textfield then the label and set constraints properly
}
}
According to the Apple specification: It is composited above the receiver’s contents and sublayers.
So, the border will always be above all subviews, even if one brings the subview to the front and so on.
So one needs to make a background view to fake the border.
similar to Stackoverflow Question
Example:
Here self is "TextField"
activeborderView is "UiView"
activeborderView.frame = CGRect.init(x: -1, y: -1, width: self.frame.size.width+2, height: self.frame.size.height+2)
activeborderView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(activeborderView)
activeborderView.topAnchor.constraint(equalTo: self.topAnchor, constant:-1).isActive = true // Place our label 10 pts above the text field
activeborderView.leftAnchor.constraint(equalTo: self.leftAnchor, constant: -1).isActive=true
activeborderView.heightAnchor.constraint(equalToConstant: self.frame.size.height+2).isActive=true
activeborderView.widthAnchor.constraint(equalToConstant: self.frame.size.width+2).isActive=true
activeborderView.layer.borderWidth = 3
activeborderView.layer.borderColor = CustomColor.blue().cgColor
activeborderView.layer.cornerRadius = 5
activeborderView.backgroundColor = .white
self.sendSubviewToBack(activeborderView)
self.setNeedsDisplay()

UIButton in a view with animation not detecting touch

I'm following a tutorial to create an interactive popup animation (http://www.swiftkickmobile.com/building-better-app-animations-swift-uiviewpropertyanimator/), and now would like to add buttons to the popup rather than have static text as in the tutorial.
The animation works fine, but the buttons are not detecting touch. Touching the button causes the animation to reverse, instead of printing my "test" statement.
From research, it looks to either be an issue with view hierarchies, the animation disabling the button touch, or layout/constraint issues with the button. I've tried addressing the above issues, but nothing has worked, was hoping someone might be able to help?
I've left out the code pertaining to the animation since I think the issue is to do with layout (and the animation seems to be working fine) - but it's still a lot; apologies in advance for the large amount of code.
class ViewController: UIViewController {
private let popupOffset: CGFloat = 440
private lazy var contentImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "Background")
return imageView
}()
private lazy var overlayView: UIView = {
let view = UIView()
view.backgroundColor = .black
view.alpha = 0
return view
}()
private lazy var popupView: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.maskedCorners = [.layerMaxXMinYCorner, .layerMinXMinYCorner]
view.layer.shadowColor = UIColor.black.cgColor
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 10
return view
}()
private lazy var closedTitleLabel: UILabel = {
let label = UILabel()
label.text = "Hello"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
return label
}()
private lazy var openTitleLabel: UILabel = {
let label = UILabel()
label.text = "Which door will you choose?"
label.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
label.textColor = UIColor.darkGray
label.textAlignment = .center
label.alpha = 0
label.transform = CGAffineTransform(scaleX: 1.6, y: 1.6).concatenating(CGAffineTransform(translationX: 0, y: 15))
return label
}()
private lazy var reviewsImageView: UIImageView = {
let imageView = UIImageView()
imageView.image = #imageLiteral(resourceName: "LabelBackground")
return imageView
}()
let stackView = UIStackView()
let buttonA = UIButton()
let buttonB = UIButton()
let buttonC = UIButton()
override func viewDidLoad() {
super.viewDidLoad()
layout()
popupView.addGestureRecognizer(panRecognizer)
}
override var prefersStatusBarHidden: Bool {
return true
}
//Layout
private var bottomConstraint = NSLayoutConstraint()
private func layout() {
contentImageView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(contentImageView)
contentImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
contentImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
contentImageView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
contentImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
overlayView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(overlayView)
overlayView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
overlayView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
overlayView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
overlayView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
popupView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(popupView)
popupView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
popupView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
bottomConstraint = popupView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: popupOffset)
bottomConstraint.isActive = true
popupView.heightAnchor.constraint(equalToConstant: 500).isActive = true
closedTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(closedTitleLabel)
closedTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
closedTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
closedTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
openTitleLabel.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(openTitleLabel)
openTitleLabel.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
openTitleLabel.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
openTitleLabel.topAnchor.constraint(equalTo: popupView.topAnchor, constant: 20).isActive = true
reviewsImageView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(reviewsImageView)
reviewsImageView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
reviewsImageView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
reviewsImageView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
reviewsImageView.heightAnchor.constraint(equalToConstant: 428).isActive = true
buttonA.backgroundColor = UIColor.clear
let heightConstraintA = buttonA.heightAnchor.constraint(equalToConstant: 135)
heightConstraintA.isActive = true
heightConstraintA.priority = UILayoutPriority(rawValue: 999)
buttonA.translatesAutoresizingMaskIntoConstraints = false
buttonA.setTitle("A", for: .normal)
buttonA.setTitleColor(UIColor.darkGray, for: .normal)
buttonA.backgroundColor = UIColor.clear
buttonA.addTarget(self, action: #selector(buttonATapped(sender:)), for: .touchDown)
//self.popupView.addSubview(buttonA)
buttonB.backgroundColor = UIColor.clear
let heightConstraintB = buttonB.heightAnchor.constraint(equalToConstant: 135)
heightConstraintB.isActive = true
heightConstraintB.priority = UILayoutPriority(rawValue: 999)
buttonB.translatesAutoresizingMaskIntoConstraints = false
buttonB.setTitle("B", for: .normal)
buttonB.setTitleColor(UIColor.darkGray, for: .normal)
buttonB.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonB)
buttonC.backgroundColor = UIColor.clear
let heightConstraintC = buttonC.heightAnchor.constraint(equalToConstant: 135)
heightConstraintC.isActive = true
heightConstraintC.priority = UILayoutPriority(rawValue: 999)
buttonC.translatesAutoresizingMaskIntoConstraints = false
buttonC.setTitle("C", for: .normal)
buttonC.setTitleColor(UIColor.darkGray, for: .normal)
buttonC.backgroundColor = UIColor.clear
//self.popupView.addSubview(buttonC)
popupView.addSubview(stackView)
stackView.backgroundColor = UIColor.clear
stackView.addArrangedSubview(buttonA)
stackView.addArrangedSubview(buttonB)
stackView.addArrangedSubview(buttonC)
stackView.translatesAutoresizingMaskIntoConstraints = false
popupView.addSubview(stackView)
stackView.leadingAnchor.constraint(equalTo: popupView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: popupView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: popupView.bottomAnchor).isActive = true
stackView.heightAnchor.constraint(equalToConstant: 428).isActive = true
stackView.axis = .vertical
stackView.distribution = .fill
stackView.translatesAutoresizingMaskIntoConstraints = false
}
#objc func buttonATapped(sender: UIButton) {
print ("test")
}
private func animateTransitionIfNeeded(to state: State, duration: TimeInterval) {
//Animation code
}
#objc private func popupViewPanned(recognizer: UIPanGestureRecognizer) {
//Animation code
}
}
***For anyone else having the same issue, here is how I solved it, thanks to #OverD:
I removed the reviewsImageView completely (because that was only for color in my case, and I can easily add the color to the UIButton instead) Then instead of adding the buttons to the popupView, I added them to the stack view, and the stack view to the popupView. Lastly, my syntax for addTarget was not correct, and for some reason changing it to touchDown instead of touchUpInside made it work.
I noticed from the provided code that you are adding the same buttons multiple times, once as a subview of the popView and once in the stackView. Also you are not assigning any targets for the buttons.
I hope this helps

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
}

UIScrollViews and anchors swift

I am having trouble setting up a scroll view and actually scrolling down. I populated the scroll view with some textfields and used anchors (topanchor,leftanchor...) to position them inside the scroll view. Even if I set the scroll view height to 1000, it wont actually move, it continues to show the same items, the scroll indicator does go down but the content itself doesnt, I already set the scroll view to scrollenabled, and delegate to self.
I think the problem might be with the anchors but then how will I arrange my items inside the scroll view, any sugestion will be greatly appreaciated.
EDIT : The code below indicates the anchors applied to the scroll view ( inputContainer ), the img corresponds to an UIImageView and the mainContainer to the UIView containing the img and the inputContainer.
inputContainer.topAnchor.constraintEqualToAnchor( img.bottomAnchor ).active = true
inputContainer.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
inputContainer.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor ).active = true
inputContainerBottomConstraint = inputContainer.bottomAnchor.constraintEqualToAnchor( cancelButton.topAnchor )
inputContainerBottomConstraint?.active = true
EDIT: This is how the code looks like :
class SView : UIView, UITextFieldDelegate, UIScrollViewDelegate {
let mainContainer : UIView = {
let v = UIView()
v.backgroundColor = .whiteColor()
v.layer.cornerRadius = 8
v.layer.masksToBounds = true
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
let Img : UIImageView = {
let img = UIImageView()
img.image = UIImage(named: "noImage")
img.backgroundColor = .blueColor()
img.translatesAutoresizingMaskIntoConstraints = false
img.contentMode = .ScaleAspectFill
img.clipsToBounds = true
return img
}()
let inputContainer : UIScrollView = {
let ic = UIScrollView()
ic.backgroundColor = .whiteColor()
ic.translatesAutoresizingMaskIntoConstraints = false
return ic
}()
let datePickerTextField : UITextField = {
let tf = UITextField()
tf.placeholder = "Fecha"
tf.textAlignment = .Center
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
let tagsTextField : UITextField = {
let tf = UITextField()
tf.placeholder = "Tags"
tf.textAlignment = .Center
tf.clearButtonMode = .Always
tf.translatesAutoresizingMaskIntoConstraints = false
return tf
}()
lazy var cancelButton : UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.rgb(255, green: 65, blue: 65, alpha: 1)
button.setTitle("Cancelar", forState: .Normal)
button.tintColor = .whiteColor()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget( self , action: #selector(handleCancelButtonPressed), forControlEvents: .TouchUpInside)
return button
}()
lazy var publicarButton : UIButton = {
let button = UIButton()
button.backgroundColor = UIColor.rgb(0 , green: 204, blue: 102, alpha: 1)
button.setTitle("Publicar", forState: .Normal)
button.tintColor = .whiteColor()
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget( self , action: #selector(handlePublicarButtonPressed), forControlEvents: .TouchUpInside)
return button
}()
override init(frame: CGRect)
{
super.init(frame: frame)
inputContainer.delegate = self
datePickerTextField.delegate = self
tagsTextField.delegate = self
setupMainContainer()
setupImg()
setupButtons()
setupInputContainer()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupMainContainer ()
{
addSubview(mainContainer)
mainContainer.centerXAnchor.constraintEqualToAnchor( centerXAnchor ).active = true
mainContainer.centerYAnchor.constraintEqualToAnchor( centerYAnchor ).active = true
mainContainer.widthAnchor.constraintEqualToAnchor( widthAnchor ).active = true
mainContainer.heightAnchor.constraintEqualToAnchor( heightAnchor )
}
func setupImg ()
{
mainContainer.addSubview(Img)
Img.topAnchor.constraintEqualToAnchor( mainContainer.topAnchor ).active = true
Img.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
Img.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor ).active = true
Img.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor , multiplier: 0.3).active = true
}
var inputContainerBottomConstraint : NSLayoutConstraint?
func setupInputContainer ()
{
mainContainer.addSubview(inputContainer)
inputContainer.topAnchor.constraintEqualToAnchor( Img.bottomAnchor ).active = true
inputContainer.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
inputContainer.rightAnchor.constraintEqualToAnchor( mainContainer.rightAnchor ).active = true
inputContainerBottomConstraint = inputContainer.bottomAnchor.constraintEqualToAnchor( cancelButton.topAnchor )
inputContainerBottomConstraint?.active = true
inputContainer.addSubview( datePickerTextField )
inputContainer.addSubview( tagsTextField )
datePickerTextField.topAnchor.constraintEqualToAnchor( inputContainer.topAnchor ).active = true
datePickerTextField.centerXAnchor.constraintEqualToAnchor( inputContainer.centerXAnchor ).active = true
datePickerTextField.widthAnchor.constraintEqualToAnchor( inputContainer.widthAnchor ).active = true
datePickerTextField.heightAnchor.constraintEqualToAnchor( inputContainer.heightAnchor, multiplier: 0.2 ).active = true
tagsTextField.bottomAnchor.constraintEqualToAnchor( inputContainer.bottomAnchor ).active = true
tagsTextField.centerXAnchor.constraintEqualToAnchor( inputContainer.centerXAnchor ).active = true
tagsTextField.widthAnchor.constraintEqualToAnchor( inputContainer.widthAnchor ).active = true
tagsTextField.heightAnchor.constraintEqualToAnchor( inputContainer.heightAnchor, multiplier: 0.2 ).active = true
}
func setupButtons()
{
mainContainer.addSubview( cancelButton )
mainContainer.addSubview( publicarButton )
cancelButton.bottomAnchor.constraintEqualToAnchor( mainContainer.bottomAnchor).active = true
cancelButton.leftAnchor.constraintEqualToAnchor( mainContainer.leftAnchor ).active = true
cancelButton.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor, multiplier: 0.5 ).active = true
cancelButton.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor, multiplier: 0.1).active = true
publicarButton.bottomAnchor.constraintEqualToAnchor( mainContainer.bottomAnchor).active = true
publicarButton.leftAnchor.constraintEqualToAnchor( cancelButton.rightAnchor ).active = true
publicarButton.widthAnchor.constraintEqualToAnchor( mainContainer.widthAnchor, multiplier: 0.5 ).active = true
publicarButton.heightAnchor.constraintEqualToAnchor( mainContainer.heightAnchor, multiplier: 0.1).active = true
} }
So when the keyboard appears the bottom anchor constant of the scroll view changes so that the keyboard "top anchor" is the new bottom anchor.
With the constraints you have described there's no way for the layout engine to determine the content height of the scroll view. You should pin your bottom text field to the bottom of the scroll view. This way the scroll view's content size will resize up to the max y of all of the text fields. Here is some code you can put in a playground to see:
import UIKit
import XCPlayground
let scrollView = UIScrollView(frame: CGRect(x: 0, y: 0, width: 200, height: 150))
scrollView.backgroundColor = UIColor.whiteColor()
let textField = UITextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.backgroundColor = UIColor.purpleColor()
let otherTextField = UITextField()
otherTextField.translatesAutoresizingMaskIntoConstraints = false
otherTextField.backgroundColor = UIColor.purpleColor()
let otherOtherTextField = UITextField()
otherOtherTextField.translatesAutoresizingMaskIntoConstraints = false
otherOtherTextField.backgroundColor = UIColor.purpleColor()
scrollView.addSubview(textField)
textField.topAnchor.constraintEqualToAnchor(scrollView.topAnchor).active = true
textField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
textField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
scrollView.addSubview(otherTextField)
otherTextField.topAnchor.constraintEqualToAnchor(textField.bottomAnchor, constant: 60).active = true
otherTextField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
otherTextField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
scrollView.addSubview(otherOtherTextField)
otherOtherTextField.topAnchor.constraintEqualToAnchor(otherTextField.bottomAnchor, constant: 60).active = true
otherOtherTextField.leadingAnchor.constraintEqualToAnchor(scrollView.leadingAnchor).active = true
otherOtherTextField.widthAnchor.constraintEqualToAnchor(scrollView.widthAnchor).active = true
otherOtherTextField.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
scrollView.setNeedsLayout()
XCPlaygroundPage.currentPage.liveView = scrollView
This places three text fields in a scroll view with 60 points between the center and top and bottom. If you comment out:
otherOtherTextField.bottomAnchor.constraintEqualToAnchor(scrollView.bottomAnchor).active = true
The scroll view in the assistant editor will not scroll, but with it it does.

Resources