提问人:피우리 提问时间:11/8/2023 最后编辑:HangarRash피우리 更新时间:11/12/2023 访问量:59
如何在stackview和tableviewcell中设置textview的动态高度?
How can I set dynamic height of textview inside of stackview and tableviewcell?
问:
我正在开发待办事项应用程序,但我不知道该怎么做。 我已经做了它的每一个解决方案。(isScrollEnabled = false, .sizetofit(), translaste~ =true ...)
如何在stackview和tableviewcell中设置textview的动态高度? 我想将textview的基本高度大于50,然后动态更改textview高度。
我在下面附上了我的代码。
class ToDoTableViewCell: UITableViewCell, UITextViewDelegate {
lazy var backColorView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let memoString: UITextView = {
let textView = UITextView()
textView.font = .systemFont(ofSize: 17, weight: .regular)
textView.layer.cornerRadius = 0
textView.layer.borderWidth = 1
textView.layer.borderColor = #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)
textView.backgroundColor = .clear
textView.isS
return textView
}()
let dateString: UILabel = {
let date = UILabel()
date.font = .systemFont(ofSize: 14, weight: .light)
date.text = "2021-11-12"
return date
}()
let updateButton: UIButton = {
let button = UIButton()
button.setTitle("UPDATE", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 9, weight: .bold)
button.layer.cornerRadius = 10
button.backgroundColor = .gray
button.setImage(UIImage(systemName: "pencil"), for: .normal)
return button
}()
let totalStackView: UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fill
stack.alignment = .fill
stack.spacing = 10
return stack
}()
let subStackView: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .fill
stack.alignment = .fill
stack.spacing = 0
return stack
}()
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
memoString.delegate = self
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
setupStackView()
setConstraints()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupStackView() {
self.contentView.addSubview(backColorView)
totalStackView.addArrangedSubview(memoString)
totalStackView.addArrangedSubview(subStackView)
subStackView.addArrangedSubview(dateString)
subStackView.addArrangedSubview(updateButton)
self.contentView.addSubview(totalStackView)
}
func setConstraints() {
totalStackView.translatesAutoresizingMaskIntoConstraints = false
subStackView.translatesAutoresizingMaskIntoConstraints = false
updateButton.translatesAutoresizingMaskIntoConstraints = false
memoString.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
totalStackView.leadingAnchor.constraint(equalTo: backColorView.leadingAnchor, constant: 10),
totalStackView.trailingAnchor.constraint(equalTo: backColorView.trailingAnchor, constant: -10),
totalStackView.topAnchor.constraint(equalTo: backColorView.topAnchor, constant: 10),
totalStackView.bottomAnchor.constraint(equalTo: backColorView.bottomAnchor, constant: -10),
subStackView.heightAnchor.constraint(equalToConstant: 30),
updateButton.widthAnchor.constraint(equalToConstant: 80),
backColorView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 25),
backColorView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -25),
backColorView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
backColorView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
memoString.heightAnchor.constraint(greaterThanOrEqualToConstant: 50)
])
}
}
class ViewController: UIViewController {
private let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
tableView.delegate = self
tableView.dataSource = self
subviews()
constraints()
setupTableView()
makeUI()
makeButton()
}
func setupTableView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(ToDoTableViewCell.self, forCellReuseIdentifier: "cell")
}
}
extension ViewController {
func makeUI() {
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithOpaqueBackground()
navigationController?.navigationBar.standardAppearance = navigationBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navigationBarAppearance
navigationController?.navigationBar.tintColor = .blue
navigationItem.scrollEdgeAppearance = navigationBarAppearance
navigationItem.standardAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
navigationController?.setNeedsStatusBarAppearanceUpdate()
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.prefersLargeTitles = true
//navigationController?.navigationBar.backgroundColor = .white
title = "메모"
}
func makeButton() {
let button = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(action))
navigationItem.rightBarButtonItem = button
}
@objc func action() {
}
func subviews() {
view.addSubview(tableView)
}
func constraints() {
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}
extension ViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 5
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return UITableView.automaticDimension
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
return cell
}
}
我希望 textview 的基本高度大于 50,然后动态更改 textview 高度。
isScrollEnabled = false, .sizetofit(), translaste~ =true ...
答:
考虑到内容,您可以获取具有给定宽度的 UITextView 的高度。
func textViewHeight(textView: UITextView) -> CGFloat {
let width = textView.frame.width
let fitSize = textView.sizeThatFits(.init(width: width, height: .greatestFiniteMagnitude))
return fitSize.height
}
在 textViewDidChange(_:) 中调用它获取高度后,您可以更新 textView 的高度约束。
几件事...
awakeFromNib()
仅在从 Storyboard Prototype 或 XIB 加载的单元格上调用,因此我们可以将其删除。在 中设置文本视图的委托。init(style: UITableViewCell.CellStyle, reuseIdentifier: String?)
您确实想在文本视图上进行设置。这将使它自动调整其高度以适合文本。.isScrollEnabled = false
当约束设置正确时(正如您所做的那样),就没有必要实现 - 事实上,这会适得其反。heightForRowAt
当单元格的高度在显示时动态更改时,我们希望调用以告诉表视图重新布局其单元格。一种非常常见的方法是在单元中设置一个将“回调”控制器的单元。tableView.performBatchUpdates(nil)
Closure
我们可以在单元类中实现,我们将调用该类为 Closure。textViewDidChange
因此,首先,在单元格类中,我们添加以下属性:
var basicClosure: (() -> ())?
并实施:
func textViewDidChange(_ textView: UITextView) {
// inform the controller that the memo text changed
basicClosure?()
}
然后在控制器中,我们设置闭包:cellForItemAt
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ToDoTableViewCell
cell.basicClosure = {
self.tableView.performBatchUpdates(nil)
}
return cell
}
现在,当您在单元格的文本视图中编辑文本时,其高度(以及它所在的单元格)将自动调整。
不过,我们需要做一些额外的事情,因为我们需要保存编辑后的文本......如果我们不这样做,那么当我们将单元格滚动到视图之外时,任何编辑都将丢失。
因此,让我们将 Closure 更改为:
// Closure so we can inform the controller when the text is edited
public var memoEdited: ((UITableViewCell, String) -> ())?
并在以下位置实现:textViewDidChange
func textViewDidChange(_ textView: UITextView) {
// inform the controller that the memo text changed
memoEdited?(self, textView.text ?? "")
}
然后我们将像这样设置该闭包:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ToDoTableViewCell
// assuming we have a data source with a ",memo" String property
cell.memoString.text = myData[indexPath.row].memo
cell.memoEdited = { [weak self] theCell, theText in
guard let self = self,
let idx = self.tableView.indexPath(for: theCell)
else { return }
// update the data with the edited text
myData[idx.row].memo = theText
// tell the table view to re-layout its cells
self.tableView.performBatchUpdates(nil)
}
return cell
}
以下是您发布的代码,经过编辑以包含这些更改:
/// basic data structure
struct ToDoStruct {
var date: Date = Date()
var memo: String = ""
}
class ToDoTableViewCell: UITableViewCell, UITextViewDelegate {
// Closure so we can inform the controller when the text is edited
public var memoEdited: ((UITableViewCell, String) -> ())?
lazy var backColorView: UIView = {
let view = UIView()
view.backgroundColor = .yellow
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
let memoString: UITextView = {
let textView = UITextView()
textView.font = .systemFont(ofSize: 17, weight: .regular)
textView.layer.cornerRadius = 0
textView.layer.borderWidth = 1
textView.layer.borderColor = #colorLiteral(red: 0.2588235438, green: 0.7568627596, blue: 0.9686274529, alpha: 1)
textView.backgroundColor = .clear
// disable scrolling
textView.isScrollEnabled = false
return textView
}()
let dateString: UILabel = {
let date = UILabel()
date.font = .systemFont(ofSize: 14, weight: .light)
date.text = "2021-11-12"
return date
}()
let updateButton: UIButton = {
let button = UIButton()
button.setTitle("UPDATE", for: .normal)
button.setTitleColor(.white, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 9, weight: .bold)
button.layer.cornerRadius = 10
button.backgroundColor = .gray
button.setImage(UIImage(systemName: "pencil"), for: .normal)
return button
}()
let totalStackView: UIStackView = {
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fill
stack.alignment = .fill
stack.spacing = 10
return stack
}()
let subStackView: UIStackView = {
let stack = UIStackView()
stack.axis = .horizontal
stack.distribution = .fill
stack.alignment = .fill
stack.spacing = 0
return stack
}()
// this is not called when using a cell that is
// NOT coming from a Storyboard Prototype or XIB
//override func awakeFromNib() {
// super.awakeFromNib()
// // Initialization code
// memoString.delegate = self
//}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
// Configure the view for the selected state
}
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
setupStackView()
setConstraints()
memoString.delegate = self
}
func textViewDidChange(_ textView: UITextView) {
// inform the controller that the memo text changed
memoEdited?(self, textView.text ?? "")
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupStackView() {
self.contentView.addSubview(backColorView)
totalStackView.addArrangedSubview(memoString)
totalStackView.addArrangedSubview(subStackView)
subStackView.addArrangedSubview(dateString)
subStackView.addArrangedSubview(updateButton)
self.contentView.addSubview(totalStackView)
}
func setConstraints() {
totalStackView.translatesAutoresizingMaskIntoConstraints = false
subStackView.translatesAutoresizingMaskIntoConstraints = false
updateButton.translatesAutoresizingMaskIntoConstraints = false
memoString.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
totalStackView.leadingAnchor.constraint(equalTo: backColorView.leadingAnchor, constant: 10),
totalStackView.trailingAnchor.constraint(equalTo: backColorView.trailingAnchor, constant: -10),
totalStackView.topAnchor.constraint(equalTo: backColorView.topAnchor, constant: 10),
totalStackView.bottomAnchor.constraint(equalTo: backColorView.bottomAnchor, constant: -10),
subStackView.heightAnchor.constraint(equalToConstant: 30),
updateButton.widthAnchor.constraint(equalToConstant: 80),
backColorView.leadingAnchor.constraint(equalTo: self.contentView.leadingAnchor, constant: 25),
backColorView.trailingAnchor.constraint(equalTo: self.contentView.trailingAnchor, constant: -25),
backColorView.topAnchor.constraint(equalTo: self.contentView.topAnchor, constant: 10),
backColorView.bottomAnchor.constraint(equalTo: self.contentView.bottomAnchor, constant: -10),
memoString.heightAnchor.constraint(greaterThanOrEqualToConstant: 50)
])
}
}
class TestToDoViewController: UIViewController {
var myData: [ToDoStruct] = []
private let tableView = UITableView()
override func viewDidLoad() {
super.viewDidLoad()
// generate some sample data
var d: Date = Date()
for i in 0..<10 {
myData.append(ToDoStruct(date: d, memo: "Memo item \(i)"))
// add 1 day
d = d.addingTimeInterval(60 * 60 * 24)
}
tableView.delegate = self
tableView.dataSource = self
subviews()
constraints()
setupTableView()
makeUI()
makeButton()
}
func setupTableView() {
tableView.translatesAutoresizingMaskIntoConstraints = false
tableView.register(ToDoTableViewCell.self, forCellReuseIdentifier: "cell")
// we probably want to dismiss the keyboard when scrolling
tableView.keyboardDismissMode = .onDrag
}
}
extension TestToDoViewController {
func makeUI() {
let navigationBarAppearance = UINavigationBarAppearance()
navigationBarAppearance.configureWithOpaqueBackground()
navigationController?.navigationBar.standardAppearance = navigationBarAppearance
navigationController?.navigationBar.scrollEdgeAppearance = navigationBarAppearance
navigationController?.navigationBar.tintColor = .blue
navigationItem.scrollEdgeAppearance = navigationBarAppearance
navigationItem.standardAppearance = navigationBarAppearance
navigationItem.compactAppearance = navigationBarAppearance
navigationController?.setNeedsStatusBarAppearanceUpdate()
navigationController?.navigationBar.isTranslucent = false
navigationController?.navigationBar.prefersLargeTitles = true
//navigationController?.navigationBar.backgroundColor = .white
title = "메모"
}
func makeButton() {
let button = UIBarButtonItem(barButtonSystemItem: .add, target: self, action: #selector(action))
navigationItem.rightBarButtonItem = button
}
@objc func action() {
}
func subviews() {
view.addSubview(tableView)
}
func constraints() {
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.topAnchor),
tableView.leftAnchor.constraint(equalTo: view.leftAnchor),
tableView.rightAnchor.constraint(equalTo: view.rightAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
])
}
}
extension TestToDoViewController: UITableViewDelegate, UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return myData.count
}
// do NOT implement heightForRowAt
//func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
// return UITableView.automaticDimension
//}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! ToDoTableViewCell
let df = DateFormatter()
df.dateFormat = "YYYY-MM-dd"
cell.dateString.text = df.string(from: myData[indexPath.row].date)
cell.memoString.text = myData[indexPath.row].memo
cell.memoEdited = { [weak self] theCell, theText in
guard let self = self,
let idx = self.tableView.indexPath(for: theCell)
else { return }
// update the data with the edited text
myData[idx.row].memo = theText
// tell the table view to re-layout its cells
self.tableView.performBatchUpdates(nil)
}
return cell
}
}
您的单元格中有一个标有“更新”的按钮,但不清楚您想用它做什么。例如,如果您想使用它来将编辑后的“备忘录”保存到存储中,则需要添加另一个 Closure 来处理“更新”按钮点击。
评论