UITextView - điều chỉnh kích thước dựa trên nội dung trong SwiftUI


8

Tôi đang cố gắng tìm ra cách làm cho kích thước UITextView phụ thuộc vào nội dung của nó trong SwiftUI. Tôi đã gói UITextView UIViewRepresentablenhư sau:

struct TextView: UIViewRepresentable {

    @Binding var showActionSheet: Bool

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextView {

        let uiTextView = UITextView()
        uiTextView.delegate = context.coordinator

        uiTextView.font = UIFont(name: "HelveticaNeue", size: 15)
        uiTextView.isScrollEnabled = true
        uiTextView.isEditable = true
        uiTextView.isUserInteractionEnabled = true
        uiTextView.backgroundColor = UIColor(white: 0.0, alpha: 0.05)
        uiTextView.isEditable = false

        return uiTextView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        uiView.attributedText = prepareText(question: question)

        var frame = uiView.frame
        frame.size.height = uiView.contentSize.height
        uiView.frame = frame
    }

    func prepareText(text: string) -> NSMutableAttributedString {
        ...................
        return attributedText
    }

    class Coordinator : NSObject, UITextViewDelegate {

        var parent: TextView

        init(_ view: TextView) {
            self.parent = view
        }

        func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange) -> Bool {
            parent.showActionSheet = true
            return false
        }
    }
}

Hơn nữa, tôi đã cố gắng thay đổi kích thước khung hình trong updateUIView dựa trên kích thước nội dung của nó, nhưng nó không có tác dụng gì. Tôi cho rằng ở giai đoạn này, khung nhìn thậm chí không được bố trí và khung của nó đang bị ghi đè ở một nơi khác. Tôi thực sự sẽ đánh giá cao nếu ai đó có thể chỉ cho tôi một hướng chính xác.

Câu trả lời:


2

Tôi đã có thể làm cho điều này hoạt động bằng cách liên kết một biến trong TextView được sử dụng bởi chế độ xem đóng gói để đặt chiều cao khung. Dưới đây là triển khai TextView tối thiểu:

struct TextView: UIViewRepresentable {

    @Binding var text: String?
    @Binding var attributedText: NSAttributedString?
    @Binding var desiredHeight: CGFloat

    func makeCoordinator() -> Coordinator {
        Coordinator(self)
    }

    func makeUIView(context: Context) -> UITextView {

        let uiTextView = UITextView()
        uiTextView.delegate = context.coordinator

        // Configure text view as desired...
        uiTextView.font = UIFont(name: "HelveticaNeue", size: 15)

        return uiTextView
    }

    func updateUIView(_ uiView: UITextView, context: Context) {
        if self.attributedText != nil {
            uiView.attributedText = self.attributedText
        } else {
            uiView.text = self.attributedText
        }

        // Compute the desired height for the content
        let fixedWidth = uiView.frame.size.width
        let newSize = uiView.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

        DispatchQueue.main.async {
            self.desiredHeight = newSize.height
        }
    }

    class Coordinator : NSObject, UITextViewDelegate {

        var parent: TextView

        init(_ view: TextView) {
            self.parent = view
        }

        func textViewDidEndEditing(_ textView: UITextView) {
            DispatchQueue.main.async {
                self.parent.text = textView.text
                self.parent.attributedText = textView.attributedText
            }
        }
    }
}

Chìa khóa là sự ràng buộc của mong muốn, được tính toán trong updateUIView bằng phương thức UIView sizeThatFits. Lưu ý rằng điều này được gói trong một khối DispatchQueue.main.async để tránh lỗi "Sửa đổi trạng thái trong khi cập nhật chế độ xem".

Bây giờ tôi có thể sử dụng chế độ xem này trong ContentView của mình:

struct ContentView: View {

    @State private var notes: [String?] = [
        "This is a short Note",
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet consectetur. Morbi enim nunc faucibus a. Nunc pulvinar sapien et ligula ullamcorper malesuada proin libero.",
    ]

    @State private var desiredHeight: [CGFloat] = [0, 0]

    var body: some View {
        List {
            ForEach(0..<notes.count, id: \.self) { index in
                TextView(
                    desiredHeight: self.$desiredHeight[index],
                    text: self.$notes[index],
                    attributedText: .constant(nil)
                )
                .frame(height: max(self.desiredHeight[index], 100))
            }
        }
    }
}

Ở đây tôi có một vài ghi chú trong một mảng String, cùng với một mảng các giá trị mong muốn để liên kết với TextView. Chiều cao của TextView được đặt trong bộ sửa đổi khung trên TextView. Trong ví dụ này, tôi cũng đặt chiều cao tối thiểu để cung cấp một số khoảng trống cho chỉnh sửa nội bộ. Chiều cao khung hình chỉ cập nhật khi một trong các giá trị Trạng thái (trong trường hợp này là ghi chú) thay đổi. Trong quá trình triển khai TextView tại đây, điều này chỉ xảy ra khi quá trình chỉnh sửa kết thúc trên chế độ xem văn bản.

Tôi đã thử cập nhật văn bản trong hàm ủy nhiệm textViewDidChange trong Điều phối viên. Điều này cập nhật chiều cao khung hình khi bạn thêm văn bản, nhưng làm cho nó sao cho bạn chỉ có thể gõ văn bản ở cuối TextView kể từ khi cập nhật chế độ xem đặt lại điểm chèn vào cuối!

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.