I tried to make an editable uitextview with centered text that can have a maximum of 2 lines and that automatically adjusts its font size in order to fit its text within a fixed width and height.
My solution: Type some text in a UITextView, automatically copy that text and paste it in a uilabel that itself automatically adjusts its font size perfectly, and then retrieve the newly adjusted font size of the uilabel and set that size on the UITextView text.
I have spent about a month on this and repeatedly failed. I can't find a way to make it work. My attempted textview below glitches some letters out and hides large portions of out-of-bounds text instead of resizing everything. Please help me stack overflow Gods.
My attempt:
import UIKit
class TextViewViewController: UIViewController{
private let editableUITextView: UITextView = {
let tv = UITextView()
tv.font = UIFont.systemFont(ofSize: 20)
tv.text = "Delete red text, and type here"
tv.backgroundColor = .clear
tv.textAlignment = .center
tv.textContainer.maximumNumberOfLines = 2
tv.textContainer.lineBreakMode = .byWordWrapping
tv.textColor = .red
return tv
}()
private let correctTextSizeLabel: UILabel = {
let tv = UILabel()
tv.font = UIFont.systemFont(ofSize: 20)
tv.backgroundColor = .clear
tv.text = "This is properly resized"
tv.adjustsFontSizeToFitWidth = true
tv.lineBreakMode = .byTruncatingTail
tv.numberOfLines = 2
tv.textAlignment = .center
tv.textColor = .green
return tv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.white
view.addSubview(correctTextSizeLabel)
view.addSubview(editableUITextView)
editableUITextView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
editableUITextView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
editableUITextView.heightAnchor.constraint(equalToConstant: 150).isActive = true
editableUITextView.widthAnchor.constraint(equalToConstant: 150).isActive = true
editableUITextView.translatesAutoresizingMaskIntoConstraints = false
editableUITextView.delegate = self
correctTextSizeLabel.leftAnchor.constraint(equalTo: editableUITextView.leftAnchor).isActive = true
correctTextSizeLabel.rightAnchor.constraint(equalTo: editableUITextView.rightAnchor).isActive = true
correctTextSizeLabel.topAnchor.constraint(equalTo: editableUITextView.topAnchor).isActive = true
correctTextSizeLabel.bottomAnchor.constraint(equalTo: editableUITextView.bottomAnchor).isActive = true
correctTextSizeLabel.translatesAutoresizingMaskIntoConstraints = false
editableUITextView.isScrollEnabled = false
}
func getApproximateAdjustedFontSizeOfLabel(label: UILabel) -> CGFloat {
if label.adjustsFontSizeToFitWidth == true {
var currentFont: UIFont = label.font
let originalFontSize = currentFont.pointSize
var currentSize: CGSize = (label.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: currentFont])
while currentSize.width > label.frame.size.width * 2 && currentFont.pointSize > (originalFontSize * label.minimumScaleFactor) {
currentFont = currentFont.withSize(currentFont.pointSize - 1)
currentSize = (label.text! as NSString).size(withAttributes: [NSAttributedString.Key.font: currentFont])
}
return currentFont.pointSize
} else {
return label.font.pointSize
}
}
}
//MARK: - UITextViewDelegate
extension TextViewViewController : UITextViewDelegate {
private func textViewShouldBeginEditing(_ textView: UITextView) {
textView.becomeFirstResponder()
}
func textViewDidBeginEditing(_ textView: UITextView) {
}
func textViewDidEndEditing(_ textView: UITextView) {
}
func textViewDidChange(_ textView: UITextView) {
textView.becomeFirstResponder()
self.correctTextSizeLabel.text = textView.text
self.correctTextSizeLabel.isHidden = false
let estimatedTextSize = self.getApproximateAdjustedFontSizeOfLabel(label: self.correctTextSizeLabel)
print("estimatedTextSize: ",estimatedTextSize)
self.editableUITextView.font = UIFont.systemFont(ofSize: estimatedTextSize)
}
}
UITextField's have the option to automatically adjust font size to fit a fixed width but they only allow 1 line of text, I need it to have a maximum of 2. UILabel's solve this problem perfectly but they aren't editable.
After some searching, this looks like it will be very difficult to get working as desired.
This doesn't directly answer your question, but it may be an option:
Here's the example code:
class TestInputViewController: UIViewController {
let testLabel: InputLabel = InputLabel()
override func viewDidLoad() {
super.viewDidLoad()
let instructionLabel = UILabel()
instructionLabel.textAlignment = .center
instructionLabel.text = "Tap yellow label to edit..."
let centeringFrameView = UIView()
// label properties
let fnt: UIFont = .systemFont(ofSize: 32.0)
testLabel.isUserInteractionEnabled = true
testLabel.font = fnt
testLabel.adjustsFontSizeToFitWidth = true
testLabel.minimumScaleFactor = 0.25
testLabel.numberOfLines = 2
testLabel.setContentHuggingPriority(.required, for: .vertical)
let minLabelHeight = ceil(fnt.lineHeight)
// so we can see the frames
centeringFrameView.backgroundColor = .red
testLabel.backgroundColor = .yellow
[centeringFrameView, instructionLabel, testLabel].forEach {
$0.translatesAutoresizingMaskIntoConstraints = false
}
view.addSubview(instructionLabel)
view.addSubview(centeringFrameView)
centeringFrameView.addSubview(testLabel)
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// instruction label centered at top
instructionLabel.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
instructionLabel.centerXAnchor.constraint(equalTo: g.centerXAnchor),
// centeringFrameView 20-pts from instructionLabel bottom
centeringFrameView.topAnchor.constraint(equalTo: instructionLabel.bottomAnchor, constant: 20.0),
// Leading / Trailing with 20-pts "padding"
centeringFrameView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
centeringFrameView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// test label centered vertically in centeringFrameView
testLabel.centerYAnchor.constraint(equalTo: centeringFrameView.centerYAnchor, constant: 0.0),
// Leading / Trailing with 20-pts "padding"
testLabel.leadingAnchor.constraint(equalTo: centeringFrameView.leadingAnchor, constant: 20.0),
testLabel.trailingAnchor.constraint(equalTo: centeringFrameView.trailingAnchor, constant: -20.0),
// height will be zero if label has no text,
// so give it a min height of one line
testLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: minLabelHeight),
// centeringFrameView height = 3 * minLabelHeight
centeringFrameView.heightAnchor.constraint(equalToConstant: minLabelHeight * 3.0)
])
// to handle user input
testLabel.editCallBack = { [weak self] str in
guard let self = self else { return }
self.testLabel.text = str
}
testLabel.doneCallBack = { [weak self] in
guard let self = self else { return }
// do something when user taps done / enter
}
let t = UITapGestureRecognizer(target: self, action: #selector(self.labelTapped(_:)))
testLabel.addGestureRecognizer(t)
}
#objc func labelTapped(_ g: UITapGestureRecognizer) -> Void {
testLabel.becomeFirstResponder()
testLabel.inputContainerView.theTextView.text = testLabel.text
testLabel.inputContainerView.theTextView.becomeFirstResponder()
}
}
class InputLabel: UILabel {
var editCallBack: ((String) -> ())?
var doneCallBack: (() -> ())?
override var canBecomeFirstResponder: Bool {
return true
}
override var canResignFirstResponder: Bool {
return true
}
override var inputAccessoryView: UIView? {
get { return inputContainerView }
}
lazy var inputContainerView: CustomInputAccessoryView = {
let customInputAccessoryView = CustomInputAccessoryView(frame: .zero)
customInputAccessoryView.backgroundColor = .blue
customInputAccessoryView.editCallBack = { [weak self] str in
guard let self = self else { return }
self.editCallBack?(str)
}
customInputAccessoryView.doneCallBack = { [weak self] in
guard let self = self else { return }
self.resignFirstResponder()
}
return customInputAccessoryView
}()
}
class CustomInputAccessoryView: UIView, UITextViewDelegate {
var editCallBack: ((String) -> ())?
var doneCallBack: (() -> ())?
let theTextView: UITextView = {
let tv = UITextView()
tv.isScrollEnabled = false
tv.font = .systemFont(ofSize: 16)
tv.autocorrectionType = .no
tv.returnKeyType = .done
return tv
}()
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = UIColor.white
autoresizingMask = [.flexibleHeight, .flexibleWidth]
addSubview(theTextView)
theTextView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
// constraint text view with 8-pts "padding" on all 4 sides
theTextView.topAnchor.constraint(equalTo: topAnchor, constant: 8),
theTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
theTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
theTextView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -8),
])
theTextView.delegate = self
}
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if (text == "\n") {
textView.resignFirstResponder()
doneCallBack?()
}
return true
}
func textViewDidChange(_ textView: UITextView) {
editCallBack?(textView.text ?? "")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override var intrinsicContentSize: CGSize {
return .zero
}
}
Related
inputAccessoryView's background view is falling under its own textField and profile picture imageView.
It works fine on regular screen iPhones, but on new iPhones with notches it looks like this:
Here's how it looks animated when keyboard appears: Transition animation on becomeFirstResponder()
Here's my tableView in which I'm trying to add accessoryView:
import UIKit
import SDWebImage
class CommentsTableViewController: UITableViewController {
let viewModel = CommentsViewModel()
let postID: String
let postCaption: String
let postDate: Date
let postAuthor: ZoogramUser
var keyboardAccessoryView: CommentAccessoryView = {
let commentAccessoryView = CommentAccessoryView()
return commentAccessoryView
}()
init(post: UserPost) {
self.postID = post.postID
self.postCaption = post.caption
self.postDate = post.postedDate
self.postAuthor = post.author
super.init(style: .grouped)
self.tableView.register(PostCommentsTableViewCell.self, forCellReuseIdentifier: PostCommentsTableViewCell.identifier)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
self.title = "Comments"
keyboardAccessoryView.delegate = self
configureKeyboardAccessoryView()
viewModel.getComments(for: self.postID) {
self.tableView.reloadData()
}
tableView.backgroundColor = .systemBackground
tableView.keyboardDismissMode = .interactive
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = 100
tableView.allowsSelection = false
tableView.separatorStyle = .none
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
becomeFirstResponder()
}
override var inputAccessoryView: UIView? {
keyboardAccessoryView.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
keyboardAccessoryView.backgroundColor = .systemOrange
return keyboardAccessoryView
}
override var canBecomeFirstResponder: Bool {
return true
}
func configureKeyboardAccessoryView() {
guard let photoURL = AuthenticationManager.shared.getCurrentUserProfilePhotoURL() else {
return
}
keyboardAccessoryView.userProfilePicture.sd_setImage(with: photoURL)
}
}
And here's code for my CommentAccessoryView which I use to override inputAccessoryView:
import UIKit
protocol CommentAccessoryViewProtocol {
func postButtonTapped(commentText: String)
}
class CommentAccessoryView: UIView {
var delegate: CommentAccessoryViewProtocol?
var userProfilePicture: UIImageView = {
let imageView = UIImageView()
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.contentMode = .scaleAspectFit
imageView.clipsToBounds = true
imageView.backgroundColor = .secondarySystemBackground
imageView.contentMode = .scaleAspectFill
return imageView
}()
var commentTextField: AccessoryViewTextField = {
let textField = AccessoryViewTextField()
textField.translatesAutoresizingMaskIntoConstraints = false
textField.backgroundColor = .systemBackground
textField.placeholder = "Enter comment"
textField.clipsToBounds = true
textField.layer.borderWidth = 1
textField.layer.borderColor = UIColor.placeholderText.cgColor
return textField
}()
var postButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 30).isActive = true
button.heightAnchor.constraint(equalToConstant: 30).isActive = true
button.clipsToBounds = true
button.layer.cornerRadius = 30/2
button.setImage(UIImage(systemName: "arrow.up.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35)), for: .normal)
button.tintColor = .systemBlue
button.addTarget(self, action: #selector(didTapPostButton), for: .touchUpInside)
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupConstraints()
backgroundColor = .systemBackground
commentTextField.rightView = postButton
commentTextField.rightViewMode = .always
autoresizingMask = .flexibleHeight
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
setViewCornerRadius()
}
func setViewCornerRadius() {
userProfilePicture.layer.cornerRadius = userProfilePicture.frame.height / 2
commentTextField.layer.cornerRadius = commentTextField.frame.height / 2
}
func setupConstraints() {
self.addSubviews(userProfilePicture, commentTextField)
NSLayoutConstraint.activate([
userProfilePicture.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10),
userProfilePicture.centerYAnchor.constraint(equalTo: self.safeAreaLayoutGuide.centerYAnchor),
userProfilePicture.widthAnchor.constraint(equalToConstant: 40),
userProfilePicture.heightAnchor.constraint(equalToConstant: 40),
commentTextField.leadingAnchor.constraint(equalTo: userProfilePicture.trailingAnchor, constant: 10),
commentTextField.centerYAnchor.constraint(equalTo: userProfilePicture.centerYAnchor),
commentTextField.heightAnchor.constraint(equalToConstant: 40),
commentTextField.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10),
])
}
override var intrinsicContentSize: CGSize {
return CGSize.zero
}
#objc func didTapPostButton() {
guard let text = commentTextField.text else {
return
}
commentTextField.resignFirstResponder()
delegate?.postButtonTapped(commentText: text)
}
}
I've spent days trying to google a fix for that but nothing helps.
There were posts saying they were able to fix something similar by setting customView's bottom constraint to a safe area with the following method:
override func didMoveToWindow() {
if #available(iOS 11.0, *) {
if let window = window {
let bottomAnchor = bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0)
bottomAnchor.isActive = true
}
}
}
But when I use it, AutoLayout starts complaining.
UPDATE: I did what HangarRash recommended, changed CommentAccessoryView from UIView to UIInputView and centering profileImageView and textField to view itself and not to safe area. Now it's a little bit better, but seems to ignore safe area, inputAccessoryView should be above Home indicator but lies beneath it instead. Looking at last cell in TableView and Scroll indicator, it seems like TableView also isn't aware of inputAccessoryView and goes under it.
When I want to set a new long status by pressing the button, my profileStatusLabel height and TableViewHeader height as well don't change.
P.S. sorry about my English, if there are some mistakes
**ProfileHeaderView: UIView
**
import UIKit
...
private lazy var profileStatusLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = "Looking for a big, young, good looking, able to cook female gorilla"
profileStatusLabel.textColor = .gray
profileStatusLabel.font = profileNameLabel.font.withSize(14)
profileStatusLabel.textAlignment = .left
profileStatusLabel.sizeToFit()
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var setStatusButton: UIButton = {
let setStatusButton = UIButton()
setStatusButton.backgroundColor = .systemBlue
setStatusButton.layer.cornerRadius = 4
setStatusButton.layer.shadowOffset = CGSize(width: 4, height: 4)
setStatusButton.layer.shadowOpacity = 0.7
setStatusButton.layer.shadowRadius = 4
setStatusButton.layer.shadowColor = UIColor.black.cgColor
setStatusButton.setTitle("Set status", for: .normal)
setStatusButton.setTitleColor(.white, for: .normal)
setStatusButton.titleLabel?.font = setStatusButton.titleLabel?.font.withSize(14)
setStatusButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
setStatusButton.translatesAutoresizingMaskIntoConstraints = false
return setStatusButton
}()
private lazy var statusText: String = {
return statusText
}()
private func setupView() {
addSubview(profileImageView)
addSubview(profileNameLabel)
addSubview(profileStatusLabel)
addSubview(statusTextField)
addSubview(setStatusButton)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileNameLabel.topAnchor.constraint(equalTo: topAnchor, constant: 27),
profileNameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileNameLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
profileNameLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 30),
profileStatusLabel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 10),
profileStatusLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileStatusLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
profileStatusLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 49),
statusTextField.topAnchor.constraint(equalTo: profileStatusLabel.bottomAnchor, constant: 10),
statusTextField.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
statusTextField.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
statusTextField.heightAnchor.constraint(equalToConstant: 40),
setStatusButton.topAnchor.constraint(equalTo: statusTextField.bottomAnchor, constant: 16),
setStatusButton.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 16),
setStatusButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
setStatusButton.heightAnchor.constraint(equalToConstant: 50),
bottomAnchor.constraint(equalTo: setStatusButton.bottomAnchor, constant: 16),
])
}
#objc private func statusTextChanged(_ textField: UITextField) {
statusText = statusTextField.text!
}
#objc private func buttonAction() {
guard statusTextField.text != nil else {
print("Text the status before press the button")
return
}
profileStatusLabel.text = statusText
profileStatusLabel.updateConstraintsIfNeeded()
self.setNeedsUpdateConstraints()
self.layoutIfNeeded()
}
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .systemGray4
setupView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
**MyCustomHeader: UITableViewHeaderFooterView
**
import UIKit
class MyCustomHeader: UITableViewHeaderFooterView {
var profileHeaderView = ProfileHeaderView()
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
configureContents()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func configureContents() {
contentView.addSubview(profileHeaderView)
profileHeaderView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
contentView.leadingAnchor.constraint(equalTo: profileHeaderView.leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: profileHeaderView.trailingAnchor),
contentView.widthAnchor.constraint(equalTo: profileHeaderView.widthAnchor),
contentView.heightAnchor.constraint(equalTo: profileHeaderView.heightAnchor),
contentView.topAnchor.constraint(equalTo: profileHeaderView.topAnchor),
])
}
}
**ProfileViewController
**
import UIKit
class ProfileViewController: UIViewController, UIGestureRecognizerDelegate {
let postList = [robberyPost, eatingPost, elephantPost, camelPost]
var profileTableView = UITableView()
let myCustomHeader = MyCustomHeader()
let headerID = "headerId"
let headerID2 = "headerId2"
let cellID = "cellId"
let collectionCellID = "collectionCellId"
...
func setupTableView() {
profileTableView.contentInsetAdjustmentBehavior = .never
profileTableView.register(PhotosTableViewCell.self, forCellReuseIdentifier: PhotosTableViewCell.cellID)
profileTableView.register(PostTableViewCell.self, forCellReuseIdentifier: PostTableViewCell.cellID)
profileTableView.delegate = self
profileTableView.dataSource = self
profileTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(profileTableView)
}
func setupMyCustomHeader() {
profileTableView.register(MyCustomHeader.self, forHeaderFooterViewReuseIdentifier: headerID)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
profileTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
profileTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
profileTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
...
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray6
setupTableView()
setupMyCustomHeader()
setupConstraints()
}
}
extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
// MARK: - Table view data source
...
// MARK: - Table view delegate
...
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if #available(iOS 15, *) {
tableView.sectionHeaderTopPadding = 0
}
if section == 0 {
return UITableView.automaticDimension
} else {
return 50
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
let tapRecognizer = UITapGestureRecognizer(target: self, action: #selector(profileImageViewClicked(_ :)))
tapRecognizer.numberOfTapsRequired = 1
tapRecognizer.numberOfTouchesRequired = 1
tapRecognizer.delegate = self
header.profileHeaderView.profileImageView.isUserInteractionEnabled = true
header.profileHeaderView.profileImageView.addGestureRecognizer(tapRecognizer)
return header
} else {
let header2 = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID2) as! OneMoreCustomHeader
return header2
}
}
...
}
Probably I should use updateConstraints() function, but can't find the right way.
I added this code to buttonAction, buy it doesn't work
profileStatusLabel.updateConstraintsIfNeeded()
self.setNeedsUpdateConstraints()
self.layoutIfNeeded()
Any time you change the height of a table view element - cell, section header or footer view, etc - you must tell the table view to re-layout its elements.
This is commonly done with a closure.
For example, you would add this property to your custom header view class:
// closure so table layout can be updated
var contentChanged: (() -> ())?
and then set that closure in viewForHeaderInSection:
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
// set the closure
header.contentChanged = {
// tell the table view to re-layout itself so the header height can be changed
tableView.performBatchUpdates(nil)
}
return header
} else {
let header2 = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID2) as! OneMoreCustomHeader
return header2
}
}
and when you tap the "Set status" button to update the profileStatusLabel, you would use the closure to "call back" to the controller:
contentChanged?()
With the code you posted, you are embedding ProfileHeaderView in MyCustomHeader, which complicates things a little because you will need a closure in MyCustomHeader that works with another closure in ProfileHeaderView.
There really is no need to do that -- you can put all of your UI elements directly in MyCustomHeader to avoid that issue.
Here is a complete, runnable example. I changed your MyCustomHeader as described... as well as added some other code that you will probably end up needing (see the comments):
class MyCustomHeader: UITableViewHeaderFooterView {
// closure to inform the controller the status text changed
// so we can update the data and
// so the table layout can be updated
var contentChanged: ((String) -> ())?
// presumably, we'll be setting the statusText and the name from a dataSource
public var statusText: String = "" {
didSet {
profileStatusLabel.text = statusText
}
}
public var name: String = "" {
didSet {
profileNameLabel.text = name
}
}
private lazy var profileImageView: UIImageView = {
let v = UIImageView()
if let img = UIImage(systemName: "person.crop.circle") {
v.image = img
}
v.translatesAutoresizingMaskIntoConstraints = false
return v
}()
private lazy var profileNameLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = ""
profileStatusLabel.textColor = .black
profileStatusLabel.textAlignment = .left
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var profileStatusLabel: UILabel = {
let profileStatusLabel = UILabel()
profileStatusLabel.numberOfLines = 0
profileStatusLabel.text = ""
profileStatusLabel.textColor = .gray
profileStatusLabel.textAlignment = .left
profileStatusLabel.translatesAutoresizingMaskIntoConstraints = false
return profileStatusLabel
}()
private lazy var setStatusButton: UIButton = {
let setStatusButton = UIButton()
setStatusButton.backgroundColor = .systemBlue
setStatusButton.layer.cornerRadius = 4
setStatusButton.layer.shadowOffset = CGSize(width: 4, height: 4)
setStatusButton.layer.shadowOpacity = 0.7
setStatusButton.layer.shadowRadius = 4
setStatusButton.layer.shadowColor = UIColor.black.cgColor
setStatusButton.setTitle("Set status", for: .normal)
setStatusButton.setTitleColor(.white, for: .normal)
setStatusButton.setTitleColor(.lightGray, for: .highlighted)
setStatusButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
setStatusButton.translatesAutoresizingMaskIntoConstraints = false
return setStatusButton
}()
private lazy var statusTextField: UITextField = {
let statusTextField = UITextField()
statusTextField.text = ""
statusTextField.borderStyle = .roundedRect
statusTextField.backgroundColor = .white
statusTextField.translatesAutoresizingMaskIntoConstraints = false
return statusTextField
}()
private func setupView() {
contentView.addSubview(profileImageView)
contentView.addSubview(profileNameLabel)
contentView.addSubview(profileStatusLabel)
contentView.addSubview(statusTextField)
contentView.addSubview(setStatusButton)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileImageView.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 10),
profileImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
profileImageView.widthAnchor.constraint(equalToConstant: 160.0),
profileImageView.heightAnchor.constraint(equalTo: profileImageView.widthAnchor),
profileNameLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 27),
profileNameLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileNameLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
profileNameLabel.heightAnchor.constraint(equalToConstant: 30),
profileStatusLabel.topAnchor.constraint(equalTo: profileNameLabel.bottomAnchor, constant: 10),
profileStatusLabel.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
profileStatusLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
profileStatusLabel.heightAnchor.constraint(greaterThanOrEqualToConstant: 49),
statusTextField.topAnchor.constraint(equalTo: profileStatusLabel.bottomAnchor, constant: 10),
statusTextField.leadingAnchor.constraint(equalTo: profileImageView.trailingAnchor, constant: 20),
statusTextField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
statusTextField.heightAnchor.constraint(equalToConstant: 40),
setStatusButton.topAnchor.constraint(equalTo: statusTextField.bottomAnchor, constant: 16),
setStatusButton.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
setStatusButton.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
setStatusButton.heightAnchor.constraint(equalToConstant: 50),
contentView.bottomAnchor.constraint(equalTo: setStatusButton.bottomAnchor, constant: 16),
])
}
#objc private func buttonAction() {
guard let stText = statusTextField.text else {
print("Text the status before press the button")
return
}
statusTextField.resignFirstResponder()
// update statusText property
// which will also set the text in the label
statusText = stText
// call the closure, passing back the new text
contentChanged?(stText)
}
override init(reuseIdentifier: String?) {
super.init(reuseIdentifier: reuseIdentifier)
contentView.backgroundColor = .systemGray4
setupView()
setupConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
class ProfileViewController: UIViewController, UIGestureRecognizerDelegate {
// presumably, this will be loaded from saved data, along with the rest of the table data
var dataSourceStatusText: String = "Looking for a big, young, good looking, able to cook female gorilla"
var profileTableView = UITableView()
let headerID = "headerId"
let cellID = "cellId"
func setupTableView() {
profileTableView.contentInsetAdjustmentBehavior = .never
profileTableView.register(UITableViewCell.self, forCellReuseIdentifier: cellID)
profileTableView.delegate = self
profileTableView.dataSource = self
profileTableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(profileTableView)
}
func setupMyCustomHeader() {
profileTableView.register(MyCustomHeader.self, forHeaderFooterViewReuseIdentifier: headerID)
}
private func setupConstraints() {
NSLayoutConstraint.activate([
profileTableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
profileTableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
profileTableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
profileTableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGray6
setupTableView()
setupMyCustomHeader()
setupConstraints()
}
}
extension ProfileViewController: UITableViewDelegate, UITableViewDataSource {
// let's use 10 sections each with 5 rows so we can scroll the header out-of-view
func numberOfSections(in tableView: UITableView) -> Int {
return 10
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let c = tableView.dequeueReusableCell(withIdentifier: cellID, for: indexPath)
c.textLabel?.text = "\(indexPath)"
return c
}
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if #available(iOS 15, *) {
tableView.sectionHeaderTopPadding = 0
}
if section == 0 {
return UITableView.automaticDimension
} else {
return 50
}
}
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if section == 0 {
let header = tableView.dequeueReusableHeaderFooterView(withIdentifier: headerID) as! MyCustomHeader
header.name = "#KillaGorilla"
header.statusText = dataSourceStatusText
// set the closure
header.contentChanged = { [weak self] newStatus in
guard let self = self else { return }
// update the status text (probably also saving it somewhere?)
// if we don't do this, and the section header scrolls out of view,
// the *original* status text will be shown
self.dataSourceStatusText = newStatus
// tell the table view to re-layout itself so the header height can be changed
tableView.performBatchUpdates(nil)
}
return header
} else {
// this would be your other section header view
// for now, let's just use a label
let v = UILabel()
v.backgroundColor = .yellow
v.text = "Section Header: \(section)"
return v
}
}
}
Give that a try.
I have a button in my CollectionViewCell and I added an action to the button but the action does not get triggered. I am currently using the cell independently.
class BalanceCell: UICollectionViewCell {
lazy var toggleBtn: ToggleButton = {
let view = ToggleButton()
view.isUserInteractionEnabled = true
view.addTarget(self, action: #selector(onTapped), for: .touchUpInside)
return view
}()
lazy var titleLabel: LabelX = {
let view = LabelX()
view.text = "My Available Balance"
view.font = .systemFont(ofSize: 16, weight: .regular)
return view
}()
lazy var amountLabel: TextFieldX = {
let view = TextFieldX()
view.isSecureTextEntry = true
view.text = "N000,000.00"
view.textAlignment = .center
view.font = .systemFont(ofSize: 26, weight: .medium)
return view
}()
private lazy var stackView: StackViewX = {
let view = StackViewX(arrangedSubviews: [titleLabel, amountLabel])
view.alignment = .center
view.axis = .vertical
view.distribution = .fillProportionally
view.spacing = 4
return view
}()
lazy var bgImageView: ImageViewX = {
let view = ImageViewX()
view.backgroundColor = R.color.roundEdgeButtonBgColor()
return view
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
}
override func layoutSubviews() {
super.layoutSubviews()
setupViewHierarchy()
setupConstraints()
setupProperties()
translatesAutoresizingMaskIntoConstraints = false
}
func setupViewHierarchy() {
addSubview(bgImageView)
bgImageView.addSubviews([stackView, toggleBtn])
}
func setupConstraints() {
bgImageView.fillToSuperview()
stackView.centerInSuperview()
toggleBtn.anchor(verticalY: bgImageView.centerYAnchor, horizontalX: nil, paddingX: 0, paddingY: 0, width: 24, height: 24, enableInsets: false)
toggleBtn.rightAnchor.constraint(equalTo: stackView.leftAnchor, constant: -18).isActive = true
}
func setupProperties() {
bgImageView.backgroundColor = .red
bgImageView.layer.cornerRadius = 15
layer.cornerRadius = 15
}
func setup(with title: String, amount: String, bgImageViewColor: UIColor) {
titleLabel.text = title
amountLabel.text = amount
bgImageView.backgroundColor = bgImageViewColor
}
#objc private func onTapped() {
self.setSecureMode(self.toggleBtn.isSecure)
}
private func setSecureMode(_ secure: Bool) {
amountLabel.isSecureTextEntry = secure
let tempText = amountLabel.text
amountLabel.text = tempText
}
}
This is the way I use it.
lazy var balacnceView: BalanceCell = {
let view = BalanceCell()
return view
}()
ANy help as to why the Togelbutton is not getting called is appritiatedswift
The default value of isUserInteractionEnabled property is true in UIButton. You do not need to set this property to true.
However, the default value of isUserInteractionEnabled property is false in UIImageView. If you want to enable action in UIImageView (or in subviews of UIImageView), then set isUserInteractionEnabled to true.
In your code, you have put all subviews inside bgImageView. Just add this line of code:
bgImageView.isUserInteractionEnabled = true
I am attempting to create a collection of UITextField elements. I'd like the next button on the keyboard to skip to the next field and if that field is hidden from view by the keyboard, scroll it into view.
This is my attempt. It works apart from 1 aspect.
When dismissing the keyboard and then selecting another (or the same) field, the text input is partially hidden by the keyboard (see attached gif).
The meat and potatoes is within the ViewController extension.
class ViewController: UIViewController {
var activeField: UITextField?
var lastOffset: CGPoint!
var keyboardHeight: CGFloat!
let scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
let scrollViewContainer: UIStackView = {
let view = UIStackView()
view.axis = .vertical
view.spacing = 10
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(scrollView)
scrollView.addSubview(scrollViewContainer)
let totalFieldCount = 25
for i in 1...totalFieldCount {
let textField = createTextField(self, placeholder: "Field #\(i)", type: .default)
textField.returnKeyType = i < totalFieldCount ? .next : .done
textField.tag = i
scrollViewContainer.addArrangedSubview(textField)
}
NSLayoutConstraint.activate([
scrollView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
scrollView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
scrollView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
scrollView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
scrollViewContainer.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor),
scrollViewContainer.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor),
scrollViewContainer.topAnchor.constraint(equalTo: scrollView.topAnchor),
scrollViewContainer.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor),
scrollViewContainer.widthAnchor.constraint(equalTo: scrollView.widthAnchor)
])
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: UIResponder.keyboardWillShowNotification, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: UIResponder.keyboardWillHideNotification, object: nil)
scrollView.keyboardDismissMode = .interactive
}
func createTextField(_ delegate: UITextFieldDelegate?, placeholder: String, type: UIKeyboardType, isSecureEntry: Bool = false) -> UITextField {
let tf = UITextField(frame: .zero)
tf.placeholder = placeholder
tf.backgroundColor = .init(white: 0, alpha: 0.03)
tf.borderStyle = .roundedRect
tf.font = .systemFont(ofSize: 14)
tf.keyboardType = type
tf.autocapitalizationType = .none
tf.autocorrectionType = .no
tf.isSecureTextEntry = isSecureEntry
tf.heightAnchor.constraint(equalToConstant: 40).isActive = true
if let delegate = delegate {
tf.delegate = delegate
}
return tf
}
}
extension ViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
activeField = textField
lastOffset = self.scrollView.contentOffset
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
let nextTag = textField.tag + 1
if let nextResponder = textField.superview?.viewWithTag(nextTag) {
nextResponder.becomeFirstResponder()
} else {
activeField?.resignFirstResponder()
activeField = nil
}
return true
}
}
extension ViewController {
#objc func keyboardWillShow(notification: NSNotification) {
guard keyboardHeight == nil else { return }
if let keyboardSize = (notification.userInfo?[UIResponder.keyboardFrameBeginUserInfoKey] as? NSValue)?.cgRectValue {
keyboardHeight = keyboardSize.height
UIView.animate(withDuration: 0.3, animations: {
self.scrollView.contentInset.bottom = self.keyboardHeight
})
guard let activeField = activeField else { return }
let distanceToBottom = self.scrollView.frame.size.height - (activeField.frame.origin.y) - (activeField.frame.size.height)
let collapseSpace = keyboardHeight - distanceToBottom
guard collapseSpace > 0 else { return }
UIView.animate(withDuration: 0.3, animations: {
self.scrollView.contentOffset = CGPoint(x: self.lastOffset.x, y: collapseSpace + 10)
})
}
}
#objc func keyboardWillHide(notification: NSNotification) {
UIView.animate(withDuration: 0.3) {
self.scrollView.contentOffset = self.lastOffset
self.scrollView.contentInset.bottom = 0
}
keyboardHeight = nil
}
}
Replace keyboardFrameBeginUserInfoKey with keyboardFrameEndUserInfoKey
Here is my code , I am adding UITextfield programtically in scrollview. But UITextField is unable to open keyboard.
It looks like UITextField is not enabling even added user interaction enabled true.
I only use the constraints, no storyboard, no xibs. Only through Constraints Programmatically.
Below is my code :
class SignupViewController : UIViewController {
var backButton : UIButton!
var titleLabel : UILabel!
var navBarView : UIView!
var scrollView : UIScrollView!
var scrollMainView : UIView!
var emailfieldView : UIView!
var emailTextField : UITextField = UITextField() override func viewDidLoad() {
super.viewDidLoad()
setDesign()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
}func setDesign(){
setNavegationBar()
setBackgroundImage()
addScrollView()
}
func setBackgroundImage(){
let backgroundImage = UIImageView(image: UIImage(named: "loginbg.png"))
self.view.addSubview(backgroundImage)
backgroundImage.translatesAutoresizingMaskIntoConstraints = false
let leadingConst = backgroundImage.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0)
let trailingConst = backgroundImage.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
let topConst = backgroundImage.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0)
let bottomConst = backgroundImage.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
NSLayoutConstraint.activate([leadingConst,trailingConst,topConst,bottomConst])
}
func setNavegationBar(){
navigationItem.title = "Join"
view.backgroundColor = UIColor.white
navigationController?.navigationBar.isHidden = true
navigationController?.navigationBar.tintColor = UIColor.white
navigationController?.navigationBar.titleTextAttributes = [NSAttributedStringKey.foregroundColor: UIColor.white]
setNavBarView()
}
func setNavBarView(){
navBarView = UIView()
self.view.addSubview(navBarView)
navBarView.translatesAutoresizingMaskIntoConstraints = false
let guide = view.safeAreaLayoutGuide
let heightCost = navBarView.heightAnchor.constraint(equalToConstant: 64.0)
let leadingCost = navBarView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0.0)
let trailingConst = navBarView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
let topCost = navBarView.topAnchor.constraint(equalTo: guide.topAnchor, constant: 0.0)
NSLayoutConstraint.activate([trailingConst,heightCost,topCost,leadingCost])
setBackButton()
setNavTitle()
}
func setBackButton(){
backButton = UIButton(type: UIButtonType.custom)
backButton.setImage(UIImage(named: "join_back"), for: UIControlState.normal)
navBarView.addSubview(backButton)
backButton.translatesAutoresizingMaskIntoConstraints = false
let widthCost = backButton.widthAnchor.constraint(equalToConstant: 44.0)
let heightCost = backButton.heightAnchor.constraint(equalToConstant: 44.0)
let leadingCost = backButton.leadingAnchor.constraint(equalTo: navBarView.leadingAnchor, constant: 0.0)
let topCost = backButton.topAnchor.constraint(equalTo: navBarView.topAnchor, constant: 0.0)
NSLayoutConstraint.activate([widthCost,heightCost,topCost,leadingCost])
backButton.addTarget(self, action: #selector(self.backButtonPress), for: UIControlEvents.touchUpInside)
}
func setNavTitle(){
titleLabel = UILabel()
titleLabel.text = "Join Dubai Store"
titleLabel.font = UIFont(name: "Dubai-Regular", size: 22.0)
titleLabel.textAlignment = NSTextAlignment.center
titleLabel.tintColor = UIColor(hexString: "#353535")
navBarView.addSubview(titleLabel)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
let heightCost = titleLabel.heightAnchor.constraint(equalToConstant: 44.0)
let topCost = titleLabel.topAnchor.constraint(equalTo: navBarView.topAnchor, constant: 0.0)
let centerCost = titleLabel.centerXAnchor.constraint(equalTo: navBarView.centerXAnchor)
NSLayoutConstraint.activate([heightCost,topCost,centerCost])
}
#objc func backButtonPress(){
self.view.endEditing(true)
self.dismissView()
}
func addScrollView(){
scrollView = UIScrollView()
view.addSubview(scrollView)
scrollView.layer.borderWidth = 1.0
scrollView.layer.borderColor = UIColor.red.cgColor
scrollView.translatesAutoresizingMaskIntoConstraints = false
let leadingConst = scrollView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0)
let trailingConst = scrollView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
let topConst = scrollView.topAnchor.constraint(equalTo: navBarView.bottomAnchor, constant: 0)
let bottomConst = scrollView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0)
NSLayoutConstraint.activate([leadingConst,trailingConst,topConst,bottomConst])
addScrollMainView()
}
func addScrollMainView() {
scrollMainView = UIView()
scrollView.addSubview(scrollMainView)
scrollMainView.translatesAutoresizingMaskIntoConstraints = false
let leadingConst = scrollMainView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor, constant: 0)
let trailingConst = scrollMainView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
let topConst = scrollMainView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0)
let bottomConst = scrollMainView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor, constant: 0)
NSLayoutConstraint.activate([topConst,leadingConst,trailingConst,bottomConst])
emailFieldView()
}
func emailFieldView(){
emailfieldView = UIView()
emailfieldView.isUserInteractionEnabled = true
emailfieldView.translatesAutoresizingMaskIntoConstraints = false
scrollMainView.addSubview(emailfieldView)
let topCost = emailfieldView.topAnchor.constraint(equalTo: scrollMainView.topAnchor, constant: 0.0)
let leadingConst = emailfieldView.leadingAnchor.constraint(equalTo: scrollMainView.leadingAnchor, constant: 0)
let trailingConst = emailfieldView.trailingAnchor.constraint(equalTo: scrollMainView.trailingAnchor, constant: 0)
let heightCost = emailfieldView.heightAnchor.constraint(equalToConstant: 62.0)
NSLayoutConstraint.activate([trailingConst,heightCost,topCost,leadingConst])
//emailTextField = UITextField(frame: CGRect(x: 10, y: 0, width: SCREEN_WIDTH, height: 50))
emailTextField.placeholder = "Email"
emailTextField.layer.borderColor = UIColor.red.cgColor
emailTextField.layer.borderWidth = 1.0
// emailTextField.font = UIFont.systemFont(ofSize: 15)
// emailTextField.borderStyle = UITextBorderStyle.none
// emailTextField.keyboardType = UIKeyboardType.default
// emailTextField.returnKeyType = UIReturnKeyType.done
// //emailTextField.clearButtonMode = UITextFieldViewMode.whileEditing
emailTextField.isUserInteractionEnabled = true
emailTextField.allowsEditingTextAttributes = true
//emailTextField.contentVerticalAlignment = UIControlContentVerticalAlignment.center
emailTextField.addTarget(self, action: #selector(self.textFieldShouldBeginEditing), for: UIControlEvents.touchUpInside)
emailfieldView.addSubview(emailTextField)
emailTextField.translatesAutoresizingMaskIntoConstraints = false
emailTextField.contentMode = UIViewContentMode.left
emailTextField.delegate = self
let etopCost = emailTextField.topAnchor.constraint(equalTo: emailfieldView.topAnchor, constant: 0.0)
let eleadingConst = emailTextField.leadingAnchor.constraint(equalTo: emailfieldView.leadingAnchor, constant: 10)
let etrailingConst = emailTextField.trailingAnchor.constraint(equalTo: emailfieldView.trailingAnchor, constant: -10)
let eheightCost = emailTextField.heightAnchor.constraint(equalToConstant: 50.0)
NSLayoutConstraint.activate([etopCost,eleadingConst,etrailingConst,eheightCost])
self.scrollMainView.bringSubview(toFront: emailTextField)
emailTextField.isAccessibilityElement = true
} }
extension SignupViewController: UITextFieldDelegate {
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
// return NO to disallow editing.
print("TextField should begin editing method called")
return true
}
func textFieldDidBeginEditing(_ textField: UITextField) {
// became first responder
print("TextField did begin editing method called")
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
// return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end
print("TextField should snd editing method called")
return true
}
func textFieldDidEndEditing(_ textField: UITextField) {
// may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called
print("TextField did end editing method called")
}
func textFieldDidEndEditing(_ textField: UITextField, reason: UITextFieldDidEndEditingReason) {
// if implemented, called in place of textFieldDidEndEditing:
print("TextField did end editing with reason method called")
}
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
// return NO to not change text
print("While entering the characters this method gets called")
return true
}
func textFieldShouldClear(_ textField: UITextField) -> Bool {
// called when clear button pressed. return NO to ignore (no notifications)
print("TextField should clear method called")
return true
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
// called when 'return' key pressed. return NO to ignore.
print("TextField should return method called")
// may be useful: textField.resignFirstResponder()
return true
}
}
I just run your code, it looks like scrollMainView is not visible in the views' hierarchy. Just change the constraints. Here is the code:
func addScrollMainView() {
scrollMainView = UIView()
scrollView.addSubview(scrollMainView)
scrollMainView.translatesAutoresizingMaskIntoConstraints = false
let leadingConst = scrollMainView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0)
let trailingConst = scrollMainView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0)
let topConst = scrollMainView.topAnchor.constraint(equalTo: scrollView.topAnchor, constant: 0)
let bottomConst = scrollMainView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0)
NSLayoutConstraint.activate([topConst,leadingConst,trailingConst,bottomConst])
emailFieldView()
}
BTW, It is not related to your question, but it is better to create bg at the beginning. Just use the code below:
func setDesign(){
setBackgroundImage()
setNavegationBar()
addScrollView()
}