TextView not resizing properly - ios

I have a tableview cell with a textview inside it:
class DetailsCell: UITableViewCell, UITextViewDelegate {
let detailsTextView: UITextView = {
let tv = UITextView()
tv.font = UIFont(name: "AvenirNext-Medium", size: 24)
tv.isScrollEnabled = false
tv.textColor = .white
tv.backgroundColor = .clear
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
func textViewDidChange(_ textView: UITextView) {
let addTodoViewController = AddTodoViewController()
addTodoViewController.begindEndUpdate()
}
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: "DetailsCell")
addSubview(detailsTextView)
detailsTextView.topAnchor.constraint(equalTo: topAnchor, constant: 10).isActive = true
detailsTextView.bottomAnchor.constraint(equalTo: bottomAnchor).isActive = true
detailsTextView.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -10).isActive = true
detailsTextView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
backgroundColor = .black
textLabel?.isHidden = true
detailsTextView.delegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
When I load the app and the textview already has a text, assigned programmatically, the cell properly get's the right height and the textview is properly resized, but when I try to change the text of the textview, the cell and the textview don't resize. Can someone help me?
override func viewWillAppear(_ animated: Bool) {
toDoTextField.becomeFirstResponder()
addTodoTableView.estimatedRowHeight = 60
addTodoTableView.rowHeight = UITableViewAutomaticDimension
}
Textview with not initial text Image
Textview with initial text Image

UITextView does not resize itself when its content changes. You have to write some code to resize your textView and then accordingly adjust the height of your cell. Probably this can help you resizing textview to its content
or you can use some third party library instead of default UITextView like this one GrowingTextView

Related

UITableViewCell height not dynamically being calculated even with all the constraints added

I'm trying to create a custom UItableViewCell with three labels. Two of them on the left side (like the built-in title + subtitle layout) and the other one at the right side. I'm laying out the labels programatically.
class CustomCell: UITableViewCell {
static let identifier = String(describing: self)
lazy var primaryLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .body)
return label
}()
lazy var secondaryLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .footnote)
return label
}()
lazy var tertiaryLabel: UILabel = {
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.font = UIFont.preferredFont(forTextStyle: .callout)
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func setup() {
contentView.addSubview(primaryLabel)
contentView.addSubview(secondaryLabel)
contentView.addSubview(tertiaryLabel)
NSLayoutConstraint.activate([
tertiaryLabel.centerYAnchor.constraint(equalTo: centerYAnchor),
tertiaryLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -16),
tertiaryLabel.heightAnchor.constraint(equalToConstant: 18),
primaryLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
primaryLabel.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 12),
primaryLabel.heightAnchor.constraint(equalToConstant: 19),
secondaryLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
secondaryLabel.topAnchor.constraint(equalTo: primaryLabel.bottomAnchor, constant: 8),
secondaryLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 12),
// secondaryLabel.heightAnchor.constraint(equalToConstant: 15)
])
}
}
I want the cell's height to be calculated dynamically. I set the rowHeight and estimatedRowHeight property values to UITableView.automaticDimension in the view controller when I instantiate the table view.
private lazy var tableView: UITableView = {
let tableView = UITableView(frame: view.bounds, style: .grouped)
tableView.allowsSelection = false
tableView.dataSource = self
tableView.delegate = self
tableView.rowHeight = UITableView.automaticDimension
tableView.estimatedRowHeight = UITableView.automaticDimension
tableView.register(CustomCell.self, forCellReuseIdentifier: CustomCell.identifier)
return tableView
}()
But the cell still shows up like this. With the default height. Even though I have auto layout constraints laid out from top to bottom. I get no warnings in the console either.
Any idea what I am missing here?
Demo project
Two observations:
Your secondary label needs a negative constant to the bottom of the container.
Also, your estimated row height should be some reasonable fixed value (e.g 44 or something like that ... it doesn't need to be perfect, but just some reasonable value that the table can use for estimating the height of rows that haven't been presented yet). You do not want to use UITableView.automaticDimension for estimatedRowHeight, only for rowHeight.

How to get UITextView to anchor to the right of UICollectionViewCell?

I have UITextView that I want to anchor to the right of my UICollectionViewCell. I apply the following constraint: textView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
But it DOES NOT anchor all the way to the right. I then apply the following constraint but this time: textView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true and it DOES anchor all the way to the left. Why might this be?
https://imgur.com/a/uykWLw5
Here is my ChatMessageCell.
import UIKit
class ChatMessageCell: UICollectionViewCell {
let textView: UITextView = {
let tv = UITextView()
tv.text = "Some Text"
tv.font = UIFont.systemFont(ofSize: 16)
tv.translatesAutoresizingMaskIntoConstraints = false
return tv
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(textView)
textView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
textView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
textView.widthAnchor.constraint(equalToConstant: 200).isActive = true
textView.heightAnchor.constraint(equalTo: self.heightAnchor).isActive = true
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Your textView would be left-aligned. If you want your text view to remain with a width of 200 and appear to sit on the right you need to add: textView.textAlignment = .right
Also, I belive it's best to use leading and trailing constraints

Custom UITableViewCell - can't seem to add subview

I've created a custom UITableViewCell class as shown below (programmatically - no use of storyboards for this):
import UIKit
class MainGroupCell: UITableViewCell {
var groupLabel : UILabel {
let label = UILabel()
label.textColor = .black
label.text = "Test Group"
label.font = UIFont(name: "candara", size: 20)
return label
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.contentView.addSubview(groupLabel)
groupLabel.snp.makeConstraints({make in
make.center.equalTo(self.contentView)
})
}
required init?(coder aDecoder: NSCoder){
fatalError("init(coder:) has not been implemented")
}
}
And for some reason, I'm hitting the error that contentView and groupLabel are not in the same view hierarchy, but they are - I've added groupLabel as a subview to contentView as you can see. Any reason for hitting this error? I gave it a shot with regular Atuolayout API as well, instead of SnapKit, and no such luck. Feel like this might be a small mistake I'm missing. I've also attempted the equalToSuperview constraint, rather than what I have shown above, but as expected it also throws the same error - groupLabel's superview returns nil.
Error:
Unable to activate constraint with anchors <NSLayoutXAxisAnchor:0x280870b80
"UILabel:0x105e79fa0'Test Group'.centerX"> and <NSLayoutXAxisAnchor:0x280870a00
"UITableViewCellContentView:0x105fa16a0.centerX"> because they have no common ancestor.
Does the constraint or its anchors reference items in different view hierarchies? That's illegal.'
Try this ,
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
addSubview(groupLabel)
groupLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
groupLabel.leadingAnchor.constraint(equalTo: leadingAnchor,constant: 16),
groupLabel.topAnchor.constraint(equalTo: topAnchor, constant: 16),
groupLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
groupLabel.bottomAnchor.constraint(equalTo: bottomAnchor,constant: -16),
])
}
change your grouplabel like this
let groupLabel : UILabel = {
let label = UILabel()
label.textColor = .black
label.text = "Test Group"
label.font = UIFont(name: "candara", size: 20)
return label
}()
Change to
make.center.equalTo(self.contentView.snp.center)
or
make.center.equalToSuperview()
Instead of
make.center.equalTo(self.contentView)

Scrollable StackView inside a UITableViewCell - ScrollView ContentSize not being updated

I've recently started learning swift and iOS app development. I've been doing php backend and low level iOS/macOS programming till now and working with UI is a little hard for me, so please tolerate my stupidity.
If I understand this correctly, stackviews automatically space and contain its subviews in its frame. All the math and layout is done automatically by it. I have a horizontal stackview inside a custom UITableViewCell. The UIStackView is within a UIScrollView because I want the content to be scroll-able. I've set the anchors programmatically (I just can't figure out how to use the storyboard thingies). This is what the cells look like
When I load the view, the stackview doesn't scroll. But it does scroll if I select the cell at least once. The contentSize of the scrollview is set inside the layoutsubviews method of my custom cell.
My Custom Cell
class TableViewCell: UITableViewCell
{
let stackViewLabelContainer = UIStackView()
let scrollViewContainer = UIScrollView()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
{
super.init(style: style, reuseIdentifier: reuseIdentifier)
backgroundColor = .black
stackViewLabelContainer.axis = .horizontal
stackViewLabelContainer.distribution = .equalSpacing
stackViewLabelContainer.alignment = .leading
stackViewLabelContainer.spacing = 5
for _ in 1...10
{
let labelView = UILabel();
labelView.backgroundColor = tintColor
labelView.textColor = .white
labelView.text = "ABCD 123"
stackViewLabelContainer.addArrangedSubview(labelView)
}
scrollViewContainer.addSubview(stackViewLabelContainer)
stackViewLabelContainer.translatesAutoresizingMaskIntoConstraints = false
stackViewLabelContainer.leadingAnchor.constraint(equalTo: scrollViewContainer.leadingAnchor).isActive = true
stackViewLabelContainer.topAnchor.constraint(equalTo: scrollViewContainer.topAnchor).isActive = true
addSubview(scrollViewContainer)
scrollViewContainer.translatesAutoresizingMaskIntoConstraints = false
scrollViewContainer.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 10).isActive = true
scrollViewContainer.topAnchor.constraint(equalTo: topAnchor, constant: 5).isActive = true
scrollViewContainer.heightAnchor.constraint(equalTo:stackViewLabelContainer.heightAnchor).isActive = true
scrollViewContainer.trailingAnchor.constraint(equalTo: trailingAnchor).isActive = true
scrollViewContainer.showsHorizontalScrollIndicator = false
}
required init?(coder: NSCoder)
{
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews()
{
super.layoutSubviews()
scrollViewContainer.contentSize = CGSize(width: stackViewLabelContainer.frame.width, height: stackViewLabelContainer.frame.height)
}
}
Here's the TableViewController
class TableViewController: UITableViewController {
override func viewDidLoad()
{
super.viewDidLoad()
tableView.register(TableViewCell.self, forCellReuseIdentifier: "reuse_cell")
}
override func numberOfSections(in tableView: UITableView) -> Int
{
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return 5
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCell(withIdentifier: "reuse_cell") as! TableViewCell
return cell
}
override func viewDidLayoutSubviews()
{
print("called")
super.viewDidLayoutSubviews()
// let cells = tableView.visibleCells as! Array<TableViewCell>
// cells.forEach
// {
// cell in
// cell.scrollViewContainer.contentSize = CGSize(width: cell.stackViewLabelContainer.frame.width, height: cell.stackViewLabelContainer.frame.height)
//
// }
}
}
I figured out a method to make this work but it affects abstraction and it feels like a weird hack. You get the visible cells from within the UITableViewController, access each scrollview and update its contentSize. There's another fix I found by reversing dyld_shared_cache where I override draw method and stop reusing cells. Both solutions feel like they're far from "proper".
You should constraint the scrollview to the contentView of the cell.
contentView.addSubview(scrollView)
scrollView.translatesAutoresizingMaskIntoConstraints = false
scrollView.topAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.topAnchor).isActive = true
contentView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
scrollView.leadingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.leadingAnchor).isActive = true
scrollView.trailingAnchor.constraint(equalTo: contentView.safeAreaLayoutGuide.trailingAnchor).isActive = true
scrollView.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: scrollView.topAnchor).isActive = true
stackView.leadingAnchor.constraint(equalTo: scrollView.leadingAnchor).isActive = true
stackView.trailingAnchor.constraint(equalTo: scrollView.trailingAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: scrollView.bottomAnchor).isActive = true
stackView.widthAnchor.constraint(equalTo: scrollView.widthAnchor).isActive = true
Now you can loop your labels and add them as the arranged subviews
for _ in 1...10
{
let labelView = UILabel();
labelView.backgroundColor = tintColor
labelView.textColor = .white
labelView.text = "ABCD 123"
stackView.addArrangedSubview(labelView)
}

how to get a round UIbutton in tableviewcell in swift?

class CustomTableViewCell: UITableViewCell {
let nameLbl: UILabel = UILabel()
let profileBtn: UIButton = UIButton()
let aboutLbl: UILabel = UILabel()
override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
contentView.addSubview(profileBtn)
contentView.addSubview(nameLbl)
contentView.addSubview(aboutLbl)
nameLbl.translatesAutoresizingMaskIntoConstraints = false
profileBtn.translatesAutoresizingMaskIntoConstraints = false
aboutLbl.translatesAutoresizingMaskIntoConstraints = false
profileBtn.backgroundColor = UIColor.red
nameLbl.font = UIFont(name: "Arial", size: 16)
aboutLbl.font = UIFont(name: "Arial", size: 16)
profileBtn.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
profileBtn.leftAnchor.constraint(equalTo: leftAnchor, constant: 20).isActive = true
profileBtn.widthAnchor.constraint(equalToConstant: 40).isActive = true
profileBtn.heightAnchor.constraint(equalToConstant: 40).isActive = true
self.profileBtn.layer.masksToBounds = true
self.profileBtn.layer.cornerRadius = CGFloat(roundf(Float(self.profileBtn.frame.size.width/2.0)))
nameLbl.topAnchor.constraint(equalTo: topAnchor, constant: 30).isActive = true
nameLbl.leftAnchor.constraint(equalTo: leftAnchor, constant: 70).isActive = true
nameLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
aboutLbl.topAnchor.constraint(equalTo: nameLbl.bottomAnchor, constant: 10).isActive = true
aboutLbl.leftAnchor.constraint(equalTo: leftAnchor, constant: 70).isActive = true
aboutLbl.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
i want the profile button inside the cell to have a round design.but ebven setting the corener radius and marskstobounds to true i am getting a square button. what am i doing wrong any help is apreciated. thanks in advance.
You are calculating the corner radius when the profile button hasn't been laid out yet. This means the width of the profile button will be zero rendering the corner radius the same. Move the line that you set the corner radius to an overriding method of layoutSubviews – this will ensure the views and subsequent sizes have been laid out in order for you to set the appropriate corner radius.
override func layoutSubviews() {
super.layoutSubviews()
profileBtn.layer.cornerRadius = profileBtn.frame.width / 2
}
Here is my solution:
override func layoutSubviews() {
super.layoutSubviews()
self.makeItCircle()
}
func makeItCircle() {
self.yourbutton.layer.masksToBounds = true
self.yourbutton.layer.cornerRadius = CGFloat(roundf(Float(self.yourbutton.frame.size.width/2.0)))
}
self.imageView.layer.masksToBounds = true //- in main
When you initialise the cell, the button does not have any frame. So self.profileBtn.layer.cornerRadius = CGFloat(roundf(Float(self.profileBtn.frame.size.width/2.0))) results in cornerRadius to be 0.
Since you are giving 40 constant width and height to the button, you can simply do this:
self.profileBtn.layer.cornerRadius = 20.0
Also make sure to set the button to clip the bounds:
self.profileBtn.clipsToBounds = true

Resources