【问题标题】:SwiftUI how to create List with custom UIViews inside itSwiftUI 如何在其中创建带有自定义 UIViews 的列表
【发布时间】: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)
        ])
    }

}

【问题讨论】:

    标签: list uiview uikit swiftui uiviewrepresentable


    【解决方案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) {
        }
    }
    
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多