Autosized NSViewRepresentables in ScrollView overlap each other (SwiftUI)

261 views Asked by At

I am trying to display items in a ScrollView which contains several autosized NSViewRepresentables. However, it seems the items overlap each other after scrolling or when the size of its parent ScrollView is changed:

enter image description here

If I understood correctly from other great replies here (e.g. @Asperi's answer here), the ScrollView needs to know the height of its items to render it correctly. Therefore, I tried to pass the total height of the each item to its parent ScrollView, however, the items still overlap.

I've made a simple example:

struct dataItem: Identifiable {
    let id = UUID()
    let question: String
    let answer: String
}

let exampleData: [dataItem] = [dataItem(question: "Lorem Ipsum is simply....", answer: "Lorem Ips ange...ker including versions of Lorem Ipsum.") ,
                        dataItem(question:"The standard ..ranslation by H. Rackham.", answer:"The standard c.... Rackham."),
                        dataItem(question:"The standard ch...Rackham.", answer:"The stand...kham."),
                        dataItem(question:"The stand..Rackham.", answer:"The standa...kham."),
                        dataItem(question:"The stand...ackham.", answer:"The standard ...ham."),
                        dataItem(question:"The standa..H. Rackham.", answer:"The standard c...ackham."),
                        dataItem(question:"The st... H. Rackham.", answer:"The standard ch...m.")
]

struct ContentView: View {
    @State var totalHeight: CGFloat = 80
    
    var body: some View {
        ScrollView{
            ScrollViewReader{ scrollView in
                LazyVStack {
                    ForEach(exampleData, id: \.id) { item  in
                        RowView(item: item, totalHeight: $totalHeight)
// The following line makes the ScrollView jitter
//                            .frame(height: totalHeight)
                    }
                }
            }
        }
        .id(UUID().uuidString)
    }
}

struct RowView: View {
    let item: dataItem
    @Binding var totalHeight: CGFloat
    @State var optimalHeightQuestionValue: CGFloat = .zero
    @State var optimalHeightAnswerValue: CGFloat = .zero
    
    var body: some View{
        
        let optimalHeightQuestion = Binding<CGFloat>(
            get: {self.optimalHeightQuestionValue}, set: {
                self.optimalHeightQuestionValue = $0
                totalHeight = optimalHeightQuestionValue + optimalHeightAnswerValue
            })
        let optimalHeightAnswer = Binding<CGFloat>(
            get: {self.optimalHeightAnswerValue}, set: {
                self.optimalHeightAnswerValue = $0
                totalHeight = optimalHeightQuestionValue + optimalHeightAnswerValue
            })
        
        VStack{
            TextViewExt(attributedText: NSAttributedString(string: item.question), optimalHeight: optimalHeightQuestion)
                .frame(height: optimalHeightQuestionValue)
            TextViewExt(attributedText: NSAttributedString(string: item.answer), optimalHeight: optimalHeightAnswer)
                .frame(height: optimalHeightAnswerValue)
        }
    }
}


struct TextViewExt: NSViewRepresentable {
    typealias NSViewType = NSTextView
    
    var attributedText: NSAttributedString
    @Binding var optimalHeight: CGFloat
    
    func makeNSView(context: Context) -> NSTextView {
        let textView = NSTextView()
        textView.backgroundColor = .clear
        return textView
    }
    
    func updateNSView(_ nsView: NSTextView, context: Context) {
        DispatchQueue.main.async {
            nsView.textStorage?.setAttributedString(attributedText)
            nsView.translatesAutoresizingMaskIntoConstraints = true
            nsView.sizeToFit()
            self.optimalHeight = nsView.frame.height
        }
    }
    
}

Does anybody know why that's happening? Or is there something wrong with the auto-sizing of the NSTextView? Thanks a lot for any advise.

0

There are 0 answers