Height of the UITextView increases when using IQKeyboardManagers - ios

I recently started to use IQKeyboardManager in my app that I am working on, it works really well except for a little problem. The height of my text view increases when the keyboard appears(Which makes the text view go up), I know this because I am printing the height of the text view each time I click on the text view. Here is the code of my text view:
// Setup text view
func setupTextView() {
// Placeholder text and color
startStoryTextView.textColor = .gray
startStoryTextView.text = "Type here"
startStoryTextView.backgroundColor = .red
// Add some padding to the text insde the text view
startStoryTextView.textContainerInset = UIEdgeInsets(top: 15, left: 10, bottom: 15, right: 10)
startStoryTextView.font = UIFont(name: "PatrickHand-Regular", size: 23)
startStoryTextView.layer.cornerRadius = 25
popUp.addSubview(startStoryTextView)
addTextViewConstraints()
}
// Add the constraints to the text view
func addTextViewConstraints() {
startStoryTextView.translatesAutoresizingMaskIntoConstraints = false
startStoryTextView.leadingAnchor.constraint(equalTo: popUp.leadingAnchor, constant: 3).isActive = true
startStoryTextView.trailingAnchor.constraint(equalTo: popUp.trailingAnchor, constant: -3).isActive = true
startStoryTextView.topAnchor.constraint(equalTo: popUp.topAnchor, constant: 3).isActive = true
//startStoryTextView.heightAnchor.constraint(equalToConstant: 589).isActive = true
startStoryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70).isActive = true
}
// Add constraints to the pop up view
func addPopUpConstraints() {
popUp.translatesAutoresizingMaskIntoConstraints = false
popUp.widthAnchor.constraint(equalToConstant: view.frame.width).isActive = true
popUp.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 10).isActive = true
popUp.topAnchor.constraint(equalTo: view.topAnchor, constant: 200).isActive = true
popUp.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
view.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true
}
Specifying the height of the text view solves this problem but, it will create another problem when the app is used on other devices with different resolutions.
// To print the height of the text view every time the user clicks on the text view
func textViewDidBeginEditing(_ textView: UITextView) {
print(startStoryTextView.frame.height)
}

Don't worry, It is IQKeyboardManager's feature, not an error.
You use IQKeyboardManager, because you want the textView go up, when textViewDidBeginEditing.
The source code is very clear.
When you activate IQKeyboardManager, in IQKeyboardManager.swift, you call override init() {, override init() { do registerAllNotifications ,registerAllNotifications do Registering for UITextField notification.
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShowDng(_:)), name: UIKeyboardWillShow, object: nil)
// ...
keyboard is already showing. adjust position.
In method func optimizedAdjustPosition()(), the height changes
Your code is weird.
why startStoryTextView.bottomAnchor is related to view.safeAreaLayoutGuide.bottomAnchor.
startStoryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor, constant: -70).isActive = true
Since startStoryTextView.superView is popUp
popUp.addSubview(startStoryTextView)
I think it is code error. IQKeyboardManager unrelated.

Related

Setting a bottom constraint to a UITextView if needed

I'm trying to fix an issue with a UItextview which I placed at the bottom of a viewcontroller programmatically and sometimes it can clip through the bottom of the view if I don't set a constraint like so.
https://i.stack.imgur.com/YfyPi.png
Whenever I try to constraint the textview to the bottom of the safe area, the text needlessly expands too much if there's less text.
https://i.stack.imgur.com/8w1v2.png
Here's the relevant code snippets from the textview and the constraints respectively:
private let summaryTextView: UITextView = {
let summaryTextView = UITextView()
summaryTextView.textColor = .label
summaryTextView.backgroundColor = .customWhite
summaryTextView.textAlignment = .center
summaryTextView.font = UIFont.systemFont(ofSize: 24)
summaryTextView.clipsToBounds = true
summaryTextView.layer.cornerRadius = 20
summaryTextView.layer.masksToBounds = true
summaryTextView.isSelectable = false
summaryTextView.isEditable = false
summaryTextView.isScrollEnabled = false
summaryTextView.translatesAutoresizingMaskIntoConstraints = false
return summaryTextView
}()
private func setupConstraints() {
NSLayoutConstraint.activate([
imageContainerView.topAnchor.constraint(equalTo: view.topAnchor, constant: 120),
imageContainerView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
imageContainerView.heightAnchor.constraint(equalToConstant: 280),
imageContainerView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.4),
summaryTextView.topAnchor.constraint(equalTo: imageContainerView.bottomAnchor,constant: 15),
summaryTextView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 10),
summaryTextView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -10),
summaryTextView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
backgroundImage.fillSuperView(to: view)
bookCover.fillSuperView(to: imageContainerView)
}
Any help would be appreciated!
You need to set the height constraint for UITextView(). Because you are giving fixed top anchor and and bottom anchor so it stretches the textview.

Layout problems after replacing UILabel with UITextView in a UITableViewCell

I've got basic chat functionality as part of an App I'm building. It is basically a UITable View where the UITableViewCell only contains a UILabel (the chat message text) and a UIView (serving as a speech bubble, surrounding the text. Here's the code:
class ChatMessageViewCellController: UITableViewCell {
var ChatMessageText = UILabel()
var ChatBubble = UIView()
var leadingConstraint: NSLayoutConstraint!
var trailingConstraint: NSLayoutConstraint!
var isIncoming: Bool! {
didSet {
if self.isIncoming {
self.ChatBubble.backgroundColor = UIColor(named: "customGrey")
self.leadingConstraint.isActive = true
self.trailingConstraint.isActive = false
} else {
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.leadingConstraint.isActive = false
self.trailingConstraint.isActive = true
}
}
}
override func awakeFromNib() {
super.awakeFromNib()
addSubview(ChatBubble)
addSubview(ChatMessageText)
self.ChatBubble.translatesAutoresizingMaskIntoConstraints = false
self.ChatMessageText.translatesAutoresizingMaskIntoConstraints = false
self.ChatBubble.backgroundColor = UIColor(named: "customGreen")
self.ChatBubble.layer.cornerRadius = 10
self.ChatMessageText.numberOfLines = 0
self.ChatMessageText.textColor = .white
self.ChatMessageText.font = UIFont.systemFont(ofSize: 15, weight: UIFont.Weight.light)
let constraints = [
self.ChatMessageText.topAnchor.constraint(equalTo: topAnchor, constant: 16),
self.ChatMessageText.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -32),
self.ChatMessageText.widthAnchor.constraint(lessThanOrEqualToConstant: 220),
self.ChatBubble.topAnchor.constraint(equalTo: ChatMessageText.topAnchor, constant: -16),
self.ChatBubble.trailingAnchor.constraint(equalTo: ChatMessageText.trailingAnchor, constant: 16),
self.ChatBubble.bottomAnchor.constraint(equalTo: ChatMessageText.bottomAnchor, constant: 16),
self.ChatBubble.leadingAnchor.constraint(equalTo: ChatMessageText.leadingAnchor, constant: -16),
]
NSLayoutConstraint.activate(constraints)
self.leadingConstraint = self.ChatMessageText.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 32)
self.trailingConstraint = self.ChatMessageText.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -32)
}
My problem is this:
I'm not feeding the UILabel with standard strings but with NSAttributedStrings, as I'd like to get some of the links in there clickable and parts of the text selectable by the user.
So I've been told to use a UITextView instead of the UILabel. I've thus made the following 2 changes:
Changed var ChatMessageText = UILabel()to var ChatMessageText = UITextView()
Did remove self.ChatMessageText.numberOfLines = 0 as UITextView doesn't have a numberOfLines member
Xcode doesn't complain and the app compiles and runs but it completely messes with my layout and I just can't figure out why. All the constraints from the UILabel should also work for the UITextView - at least I thought so. But here's how the screen looks like.
What am I doing wrong? Do I need to add / alter constraints?
By default, a UITextView has scrolling enabled.
While this seems obvious, that allows the user to enter more lines of text than will fit in the frame, and the user can scroll the text up and down.
In order for this to happen, UIKit has to know the frame of the text view. If the frame is not set, UIKit has no way to know how many lines to display, or how wide the view should be. So unless we have given the text view a full set of constraints, auto-layout will give it a size of .zero. Even if given a width (or max width) constraint, auto-layout still doesn't know how many scrollable lines of text we want displayed.
Setting .isScrollEnabled = false on the text view changes all of that.
Now, if we only constrained the position and width of the text view, UIKit will calculate the height based on the content size of the .text property.
This can be easily demonstrated. We'll create two text views, give them each top, leading and max-width (lessThanOrEqualTo) constraints, and the same text... but set .isScrollEnabled = false on one of them:
class TextViewTestViewController: UIViewController {
let nonScrollingTextView = UITextView()
let scrollingTextView = UITextView()
override func viewDidLoad() {
super.viewDidLoad()
let s = "This is a test string to demonstrate UITextView size behavior."
[nonScrollingTextView, scrollingTextView].forEach {
tv in
tv.translatesAutoresizingMaskIntoConstraints = false
tv.font = UIFont.systemFont(ofSize: 17.0)
tv.text = s
view.addSubview(tv)
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
nonScrollingTextView.topAnchor.constraint(equalTo: g.topAnchor, constant: 40.0),
nonScrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
nonScrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
scrollingTextView.topAnchor.constraint(equalTo: nonScrollingTextView.bottomAnchor, constant: 40.0),
scrollingTextView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40.0),
scrollingTextView.widthAnchor.constraint(lessThanOrEqualToConstant: 300.0),
])
// disable scrolling on the "top" text view
nonScrollingTextView.isScrollEnabled = false
// top text view is cyan
nonScrollingTextView.backgroundColor = .cyan
// bottom text view is green (although we won't see it)
scrollingTextView.backgroundColor = .green
}
}
Result:
We've added two text views, but only disabled scrolling on the "top" one (cyan background). We don't even see the second one (green background), because auto-layout gives it a height of Zero.
Worth noting... if the text view has scrolling disabled and has editing enabled, it will automatically grow / shrink as the user adds / deletes text.

White lines in ViewController on Screen Rotation

I am new to iOS development. I encountered the problem, when I rotate the screen on iPhone X emulator I got white stripes on the side of the screen as you see on the second picture.
I have already set background for ViewController and for TableView.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = Colors.backGround
view.tintColor = Colors.backGround
It did not help. When the app starts from landscape mode the issue disappears.
Your blue view area has leading and trailing constraint to safe area.
Just make it to superview.
EDIT:
Here is code for same to achieve with swift code
//Creating tableview
let tableView = UITableView()
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.backgroundColor = .lightGray
tableView.dataSource = self
self.view.addSubview(tableView)
//Setting layout of tableview
tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor).isActive = true
tableView.topAnchor.constraint(equalTo: self.view.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor).isActive = true
//Setting insets for respect safe area
tableView.contentInset = self.view.safeAreaInsets
Here are screenshots for same.
Vertical
Horizontal
If you created UITableView Programmatically
then follow the below steps
private let tableView = UITableView()
then
func setupView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(tableView)
}
then
func setupConstraints() {
self.tableView.topAnchor.constraint(equalTo: (self.tableView.superview.safeAreaLayoutGuide.topAnchor), constant: 0).isActive = true
self.tableView.leadingAnchor.constraint(equalTo: (self.tableView.superview.leadingAnchor), constant: 0).isActive = true
self.tableView.trailingAnchor.constraint(equalTo: (self.tableView.superview.trailingAnchor), constant: 0).isActive = true
self.tableView.bottomAnchor.constraint(equalTo: (self.tableView.superview.safeAreaLayoutGuide.bottomAnchor), constant: 0).isActive = true
}
Change your leading and trailing (or left and right) constraints for tableView from safe area to superview and the table will fill the screen horizontally.
Alternatively, just set the background color of the white view to that of the table view. That will ensure your table view content is always visible because it will stay inside the safe area (I'm assuming the white view is self.view here):
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = tableView.backgroundColor
}

Text won't show with constraints added

So here is my current code for the viewdidload and the setup view func
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(bearImageView)
view.addSubview(descriptionText)
view.addSubview(startButton)
setupView()
}
#objc private func start() {
}
private func setupView() {
bearImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
bearImageView.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
bearImageView.widthAnchor.constraint(equalToConstant: 200).isActive = true
bearImageView.heightAnchor.constraint(equalToConstant: 250).isActive = true
descriptionText.topAnchor.constraint(equalTo: bearImageView.bottomAnchor, constant: 10).isActive = true
descriptionText.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
descriptionText.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
startButton.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
startButton.topAnchor.constraint(equalTo: descriptionText.bottomAnchor, constant: 140).isActive = true
startButton.widthAnchor.constraint(equalToConstant: 80).isActive = true
startButton.heightAnchor.constraint(equalToConstant: 40).isActive = true
}
Both the bearimage and button constraints work fine (minus flipping the phone horizontally but ill fix that later) however the text just refuses to show. The text are made programmatically for istance let descriptionText = UITextView = {...}() and etc. Any of you guys have an idea?
If you look closely you have missed the Height Constraint for your UITextView. If you're using a UILabel or UITextField they don't need a height constraint and can calculate their height based on it's inner contents but UITextView is not going to do that because it will start scrolling if the contents is more than it's height and that's why it can not set the height based on it's inner contents and it's height is zero by default.
add a HeightConstraint to your UITextView as well.
// This will fix your problem
descriptionText.heightAnchor.constraint(equalToConstant: 120).isActive = true
It's possible the image's intrinsic content size is so large that it is expanding such that there is no more space available for the descriptionText label. Try updating the content compression resistance priority of the label to required so it cannot be compressed by the image view.
Swift 3:
descriptionText.setContentCompressionResistancePriority(UILayoutPriorityRequired, for: .vertical)
Swift 4:
descriptionText.setContentCompressionResistancePriority(.required, for: .vertical)

Swift iOS -How to set UIView's Height Anchor <= To A Label's Intrinsic Text Size? 'NSLayoutConstraint' is not convertible to 'Bool'

I have a programmatic view with a label inside of it that I'm pinning to the bottom of the navBar. There will be dynamic text inside of the label and I want the view the label is in to be the at least 64 pts or bigger if the height of text makes it smaller.
The intrinsic size of the text from this label sets the view at a noticeable height.
setViewAndLabel(dynamicText: "Unknown Error\nPlease try your request again\Error: 123")
However the intrinsic size of this text makes the height to small:
setViewAndLabel(dynamicText: "Message Deleted!")
The Message Deleted! should be more along the lines of:
I used some return keys to set it like that but I don't think that's the correct way to go because different messages will get generated:
setViewAndLabel(dynamicText: "\nMessage Deleted!\n")
I also tried:
if myView.heightAnchor.constraint(lessThanOrEqualToConstant: 64){
myView.heightAnchor.constraint(equalToConstant: 64).isActive = true
}
But I get the error:
'NSLayoutConstraint' is not convertible to 'Bool'
What's the best way to set the height for the view the label is in to a minimum height?
var myLabel: UILabel(){
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.textColor = UIColor.white
label.font = UIFont(name: "Helvetica-Regular", size: 19)
label.numberOfLines = 0
label.sizeToFit()
label.textAlignment = .center
return label
}
let myView:UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
override func viewWillAppear(_ animated: Bool)
super.viewWillAppear(animated){
setViewAndLabel(dynamicText: //some text will get set here)
}
func setViewAndLabel(dynamicText: String){
view.addSubView(myView)
myView.backgroundColor = UIColor.red
myView.topAnchor.constraint(equalTo: view.topAnchor, constant: 64).isActive = true
View.widthAnchor.constraint(equalTo: view.widthAnchor, constant: 0).isActive = true
myView.addSubView(myLabel)
myLabel.text = dynamicText
myLabel.topAnchor.constraint(equalTo: myView.topAnchor, constant: 0).isActive = true
myLabel.widthAnchor.constraint(equalTo: myView.widthAnchor, constant: 0).isActive = true
myView.bottomAnchor.constraint(equalTo: myLabel.bottomAnchor, constant: 0).isActive = true
//this if statement doesn't work
if myView.heightAnchor.constraint(lessThanOrEqualToConstant: 64){
viewForErrorLabel.heightAnchor.constraint(equalToConstant: 64).isActive = true
}
}
This is how you have to set up your constraints:
view.addSubview(myView)
myView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true
myView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
myView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
myView.heightAnchor.constraint(greaterThanOrEqualToConstant: 64).isActive = true
myView.addSubview(myLabel)
myLabel.topAnchor.constraint(equalTo: myView.topAnchor).isActive = true
myLabel.leadingAnchor.constraint(equalTo: myView.leadingAnchor).isActive = true
myLabel.trailingAnchor.constraint(equalTo: myView.trailingAnchor).isActive = true
myLabel.bottomAnchor.constraint(equalTo: myView.bottomAnchor).isActive = true
You do not need to check the label's height at all. You can simply always create that height greater than or equal constraint for myView and its height will never be smaller than 64pt (or whatever value you set it to) - even if the label contains a very short text.

Resources