Reputation: 936
I have a UIView that increases in size once the text in a UITextView reaches a certain point. Both views are resizing, however I can't get the view to move upwards to accommodate the bottomconstraint I set on the UIView. Here's a screenshot of the issue.
I have tried using both view.setNeedsLayout()
and view.layoutIfNeeded()
but I'm assuming I might be implementing them wrong...
Here's the code for the project
import UIKit
class ViewController: UICollectionViewController, UICollectionViewDelegateFlowLayout, UITextViewDelegate {
var bottomConstraint: NSLayoutConstraint?
var phconstraint: NSLayoutConstraint?
let searchBarContainer: UIView = {
let sBarContainer = UIView()
sBarContainer.backgroundColor = UIColor.gray
sBarContainer.layer.cornerRadius = 3
return sBarContainer
}()
let searchBar: UITextView = {
let sBar = UITextView()
sBar.textAlignment = .left
sBar.font = .systemFont(ofSize: 12)
sBar.backgroundColor = UIColor.red
sBar.sizeToFit()
return sBar
}()
let placeholder: UILabel = {
let pholder = UILabel()
pholder.font = UIFont.systemFont(ofSize: 16, weight: 0.20)
pholder.frame.size = pholder.intrinsicContentSize
pholder.backgroundColor = UIColor.clear
pholder.textColor = UIColor.gray
pholder.text = "Share!"
pholder.textAlignment = .left
return pholder
}()
let dividerLine: UIView = {
let line = UIView()
line.backgroundColor = UIColor.init(red: 240/255, green: 240/255, blue: 240/255, alpha: 1)
return line
}()
let bubbleview : UIView = {
let bView = UIView()
bView.backgroundColor = UIColor.clear
return bView
}()
func setupKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardNotification), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
}
func handleKeyboardNotification(_ notification: Notification) {
if let userInfo = notification.userInfo {
let keyboardFrame = (userInfo[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue
print(keyboardFrame as Any)
let isKeyboardShowing = notification.name == NSNotification.Name.UIKeyboardWillShow
let isKeybobardNotShowing = notification.name == NSNotification.Name.UIKeyboardWillHide
bottomConstraint?.constant = isKeyboardShowing ? -keyboardFrame!.height : 0
UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed) in
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Search Bar"
collectionView?.backgroundColor = UIColor.white
setupKeyboardObservers()
self.searchBar.delegate = self
view.addSubview(searchBarContainer)
searchBarContainer.addSubview(searchBar)
searchBar.translatesAutoresizingMaskIntoConstraints = false
searchBarContainer.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint(item: searchBarContainer, attribute: .width, relatedBy: .equal, toItem: view, attribute: .width, multiplier: 0.75, constant: 0).isActive = true
NSLayoutConstraint(item: searchBarContainer, attribute: .height, relatedBy: .equal, toItem: view, attribute: .height, multiplier: 0.05, constant: 0).isActive = true
NSLayoutConstraint(item: searchBarContainer, attribute: .centerX, relatedBy: .equal, toItem: view, attribute: .centerX, multiplier: 1, constant: 0).isActive = true
bottomConstraint = NSLayoutConstraint(item: searchBarContainer, attribute: .bottom, relatedBy: .equal, toItem: view, attribute: .bottom, multiplier: 1, constant: 0)
view.addConstraint(bottomConstraint!)
}
func textViewDidChange(_ textView: UITextView) {
let currentString: String = textView.text!
let length: Int = (currentString.characters.count )
let messageText = currentString
let size = CGSize(width: searchBarContainer.frame.width , height: searchBarContainer.frame.height * 2)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
let estimatedFrame = NSString(string: messageText).boundingRect(with: size, options: options, attributes: [NSFontAttributeName: UIFont.systemFont(ofSize: 12)], context: nil)
print("this is the ewidth \(estimatedFrame.width)")
searchBarContainer.addSubview(bubbleview)
bubbleview.frame = estimatedFrame
searchBar.sizeToFit()
searchBar.frame.size.width = searchBarContainer.frame.width
if estimatedFrame.height >= (searchBarContainer.frame.height) {
searchBarContainer.frame.size.height = estimatedFrame.height
self.view.addConstraint(self.bottomConstraint!)
UIView.animate(withDuration: 0, delay: 0, options: UIViewAnimationOptions.curveEaseOut, animations: {
self.view.layoutIfNeeded()
}, completion: { (completed) in
})
}
view.setNeedsLayout()
view.layoutIfNeeded()
}
}
extension UIView {
func addConstraintsWithFormat(_ format: String, views : UIView...) {
var viewsDictionary = [String: UIView]()
for(index, view) in views.enumerated(){
let key = "v\(index)"
viewsDictionary[key] = view
view.translatesAutoresizingMaskIntoConstraints = false
}
addConstraints(NSLayoutConstraint.constraints(withVisualFormat: format, options: NSLayoutFormatOptions(), metrics: nil, views: viewsDictionary))
}
}
Upvotes: 2
Views: 529
Reputation: 77546
Couple notes...
When trying to use an "auto-sizing" UITextView
you will find it works best if you disable scrolling... so add this to your searchBar
setup:
sBar.isScrollEnabled = false
You are mixing auto-layout / constraints with explicitly setting frame sizes. That frequently gets you in trouble. Use one or the other (with constraints being the preferred method). In your code, you set
searchBar.translatesAutoresizingMaskIntoConstraints = false
But you never give it any constraints.
Edit: After playing around a bit, you can greatly simplify things just by relying on auto-layout.
Instead of using a "container" view to hold the searchBar text view, create a "padding" view that will sit underneath searchBar
. Then:
searchBar
should sit "on top of" paddingView
paddingView
should slide up with the keyboardsearchBar
will automatically slide up, because it's pinned to the top of paddingView
searchBar
's height will automatically change based on text contentSo -- paddingView
will control the position, width and Y-position.
You already have a property to control the Y-position
var bottomConstraint: NSLayoutConstraint?
Change searchBar
definition and add paddingView
definition:
let searchBar: UITextView = {
let sBar = UITextView()
sBar.textAlignment = .left
sBar.font = .systemFont(ofSize: 12)
sBar.backgroundColor = UIColor.red
sBar.isScrollEnabled = false
sBar.translatesAutoresizingMaskIntoConstraints = false
return sBar
}()
let paddingView: UIView = {
let v = UIView()
v.translatesAutoresizingMaskIntoConstraints = false
v.backgroundColor = .gray
v.layer.cornerRadius = 3.0
return v
}()
viewDidLoad
ends up like this:
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.title = "Search Bar"
collectionView?.backgroundColor = UIColor.white
setupKeyboardObservers()
self.searchBar.delegate = self
// add bottom "padding" view
view.addSubview(paddingView)
// add searchBar text view
view.addSubview(searchBar)
// set width of padding view to 75% of view width
paddingView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.75).isActive = true
// set center X of padding view to center X of view
paddingView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 1.0).isActive = true
// set height of padding view to 6.0 (we're rounding the corners with a radius of 3.0)
paddingView.heightAnchor.constraint(equalToConstant: 6.0).isActive = true
// assign a constraint to bottomConstraint property so we can change the Y-position of padding view (and searchBar) when desired
// initially set the constant to 0 (so it sits on the bottom of the view)
bottomConstraint = paddingView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: 0.0)
bottomConstraint?.isActive = true
// set searchBar width to padView width
searchBar.widthAnchor.constraint(equalTo: paddingView.widthAnchor, multiplier: 1.0).isActive = true
// set searchBar center X to padView center X
searchBar.centerXAnchor.constraint(equalTo: paddingView.centerXAnchor, constant: 0.0).isActive = true
// set searchBar bottom to padView Top + 3.0 (we want to cover the top 3 pts)
searchBar.bottomAnchor.constraint(equalTo: paddingView.topAnchor, constant: 3.0).isActive = true
}
Since UITextView
auto-adjusts its own size when using auto-layout, you don't even need the textViewDidChange
function:
func textViewDidChange(_ textView: UITextView) {
// nothing to do here...
}
Note: All I could see from your bubbleview
was that it covered a portion of the searchBar
text view... so I ignored it.
Upvotes: 2
Reputation: 1814
Update textView
layout after calling sizeToFit()
searchBar.sizeToFit()
searchBar.frame.size.width = searchBarContainer.frame.width
searchBar.setNeedsLayout()
Upvotes: 0
Reputation: 2149
Just use stackview.
Upvotes: 0