SwiftUI 如何在其中创建带有自定义 UIViews 的列表
Posted
技术标签:
【中文标题】SwiftUI 如何在其中创建带有自定义 UIViews 的列表【英文标题】:SwiftUI how to create List with custom UIViews inside it 【发布时间】:2020-03-24 10:58:14 【问题描述】:我考虑如何创建以自定义 UIViews 作为其行的 SwitUI 列表。
我创建列表:
List
RowView()
RowView 是 UIRowView 的 UIViewRepresentable
struct RowView : UIViewRepresentable
func makeUIView() -> UIRowView ...
UIRowView 是自定义视图
UIRowView: UIView ...
当前显示第一行,但它们通常没有正确布局,并且在滚动时此视图会消失而不是被回收
更新
示例 1
struct NoteView: UIViewRepresentable
// MARK: - Properties
let note: Note
let date = Date()
func makeUIView(context: Context) -> UINoteView
let view = UINoteView()
view.note = note
return view
func updateUIView(_ uiView: UINoteView, context: Context)
uiView.note = note
print("View bounds: \(uiView.bounds)")
var body: some View
List
ForEach(Array(notes.enumerated()), id: \.1) (i, note) in
NoteView(note: note)
.background(Color.green)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.background(Color.red)
示例 2 - 简化
struct TestView : UIViewRepresentable
let text : String
func makeUIView(context: Context) -> UILabel
UILabel()
func updateUIView(_ uiView: UILabel, context: Context)
uiView.text = text
var body: some View
List
ForEach(0..<30, id: \.self) i in
TestView(text: "\(i)")
两者似乎都无法正常工作,因为行消失了 如果有更多内容,我还遇到了视图不保持填充和超出屏幕的问题。只有几个第一行(最初在屏幕布局上正确可见)其他消失或跳转到某处。
更新 2
这是可自动调整的 UINoteView
class UINoteView: UIView
// MARK: - Init
override init(frame: CGRect)
super.init(frame: frame)
setupViews()
required init?(coder aDecoder: NSCoder)
super.init(coder: aDecoder)
setupViews()
// MARK: - Properties
var note: Note?
didSet
textView.attributedText = note?.content?.parsedhtmlAttributedString(textStyle: .html)
noteFooterViewModel.note = note
// MARK: - Views
lazy var textView: UITextView =
let textView = UITextView()
textView.translatesAutoresizingMaskIntoConstraints = false
textView.backgroundColor = UIColor.yellow
textView.textContainer.lineBreakMode = .byWordWrapping
textView.textContainerInset = .zero
textView.textContainer.lineFragmentPadding = 0
textView.isScrollEnabled = false
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.isEditable = false
textView.textContainer.maximumNumberOfLines = 0
return textView
()
lazy var label: UILabel =
let label = UILabel()
label.translatesAutoresizingMaskIntoConstraints = false
label.text = "TEST ROW \(note?.id ?? "")"
return label
()
lazy var vStack: UIStackView =
let stack = UIStackView(arrangedSubviews: [
textView,
noteFooter
])
stack.axis = .vertical
stack.alignment = .fill
stack.distribution = .fill
stack.translatesAutoresizingMaskIntoConstraints = false
return stack
()
var noteFooterViewModel = NoteFooterViewModel()
var noteFooter: UIView
let footer = NoteFooter(viewModel: noteFooterViewModel)
let hosting = UIHostingController(rootView: footer)
hosting.view.translatesAutoresizingMaskIntoConstraints = false
return hosting.view
private func setupViews()
self.backgroundColor = UIColor.green
self.addSubview(vStack)
NSLayoutConstraint.activate([
vStack.leadingAnchor.constraint(equalTo: self.leadingAnchor),
vStack.trailingAnchor.constraint(equalTo: self.trailingAnchor),
vStack.topAnchor.constraint(equalTo: self.topAnchor),
vStack.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
【问题讨论】:
【参考方案1】:更新的答案 试试这个:
struct TestView : UIViewRepresentable
let text : String
var label : UILabel = UILabel()
func makeUIView(context: Context) -> UILabel
return label
func updateUIView(_ uiView: UILabel, context: Context)
uiView.text = text
struct ContentView : View
var body: some View
List (0..<30, id: \.self) i in
TestView(text: "\(i)").id(i)
struct ContentView_Previews: PreviewProvider
static var previews: some View
ContentView()
【讨论】:
我在这两种情况下都添加了简化和更复杂的示例(也只留下简单的 UILabel),我在滚动列表时丢失了单元格,此外,这些单元格可以跳出屏幕。 如果我添加了 .id() 这样的 NoteView(note: note).id(i) 并将此视图放在 HStack 中,它现在不会消失,但垂直大小不合适 “没有大小”是什么意思?只需在 testview 之后设置 frame(height:blabla) ,你就可以设置任何你想要的高度 或者只是给我们一个新的可复制示例,我们可以看到它......谢谢 我希望它自动调整大小。我想要自定义视图 NoteView:UIViewRepresentable。它创建 UINoteView:UIView。并且有多行标签或多行 UITextView。我希望细胞自动获得它的高度。我事先不知道这个单元格的大小【参考方案2】:我确实喜欢下面。即使没有 UIViewRepresentable 也可以通过getTextFrame(for note)
获得帧大小
var body: some View
List
ForEach(Array(notes.enumerated()), id: \.1) (i, note) in
NoteView(note: note)
// add this
.frame(width: getTextFrame(for: note).width, height: getTextFrame(for: note).height)
.background(Color.green)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.background(Color.red)
func getTextFrame(for text: String, maxWidth: CGFloat? = nil, maxHeight: CGFloat? = nil) -> CGSize
let attributes: [NSAttributedString.Key: Any] = [
.font: UIFont.preferredFont(forTextStyle: .body)
]
let attributedText = NSAttributedString(string: text, attributes: attributes)
let width = maxWidth != nil ? min(maxWidth!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
let height = maxHeight != nil ? min(maxHeight!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
let constraintBox = CGSize(width: width, height: height)
let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral
print(rect.size)
return rect.size
struct NoteView: UIViewRepresentable
let note: String
func makeUIView(context: Context) -> UITextView
let textView = UITextView()
textView.delegate = context.coordinator
textView.font = UIFont.preferredFont(forTextStyle: .body)
textView.isEditable = false
textView.isSelectable = true
textView.isUserInteractionEnabled = true
textView.isScrollEnabled = false
textView.backgroundColor = .clear
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textView.textContainerInset = .zero
textView.textContainer.lineFragmentPadding = 0
textView.textContainer.lineBreakMode = .byWordWrapping
textView.text = note
return textView
func updateUIView(_ uiView: UITextView, context: Context)
【讨论】:
以上是关于SwiftUI 如何在其中创建带有自定义 UIViews 的列表的主要内容,如果未能解决你的问题,请参考以下文章
带有选择器的 SwiftUI 列表:选择不适用于自定义类型(macOS)
带有 UIViewRepresentable 的 SwiftUI 自定义文本字段 ObservableObject 和推送视图的问题