Search code examples
swiftswiftuiswiftui-texteditor

SwiftUI Text Editor how can I keep track of the number of lines in the editor


I have a TextEditor using SwiftUI 3.0 and every time a new line is created I want to increase the editor height by about 16 points . The issue that I have is that my code is not recognizing the new line being formed . The line count stays at 1 and the editor does not expand . I have looked at a few examples but have not gotten it to work . Any suggestions would be great . The code below is the complete code .

import SwiftUI

struct CommentTestView: View {
    @State var Posting = ""
    @State var height: CGFloat = 30
    
    var body: some View {
        VStack {
            Spacer()
            ZStack(alignment: .topLeading) {
                        Text("Placeholder")
                            .foregroundColor(.gray)
                            .font(Font.custom("Helvetica Neue", size: 13.5))
                            .padding(.horizontal, 4)
                            .padding(.vertical, 9)
                            .opacity(Posting.isEmpty ? 1 : 0)

                        TextEditor(text: $Posting)
                            .foregroundColor(.black)
                            .font(Font.custom("Helvetica Neue", size: 14))
                            .frame(height: height)
                            .opacity(Posting.isEmpty ? 0.25 : 1)
                            .onChange(of: self.Posting, perform: { value in
                                withAnimation(.easeInOut(duration: 0.1), {
                                    print("test line number: \( value.numberOfLines())" )
                                    let LineCount: CGFloat = CGFloat(value.numberOfLines())
                                    if value.numberOfLines() == 0 || value.isEmpty {
                                        // initial height
                                        height = 35
                                    } else {
                                        height = 16 * LineCount
                                    }
                                })
                            })
                    }
                    .padding(4)
                    .overlay(
                        RoundedRectangle(cornerRadius: 8)
                            .stroke(Color.gray, lineWidth: 0.5)
                    )
                }
        }
    }


struct CommentTestView_Previews: PreviewProvider {
    var hhh = TimeLineInsertions()
    static var previews: some View {
        CommentTestView()
    }
}

extension String {
    func numberOfLines() -> Int {
        return self.numberOfOccurrencesOf(string: "\n") + 1
    }

    func numberOfOccurrencesOf(string: String) -> Int {
        return self.components(separatedBy:string).count - 1
    }

}

Solution

  • I got it to work smoothly and I just needed to make a few changes . With the changes below I just track the size of the Text instead of the number of lines and update the TextEditor accordingly . For the number of lines it only worked when I would press enter .

    struct CommentVTestView: View {
        @State var Posting = ""
        @State var height: CGFloat = 30.0
        var body: some View {
            VStack(alignment: .leading) {
                
                TextEditor(text: $Posting)
                    .foregroundColor(.black)
                    .font(Font.custom("Helvetica Neue", size: 15))
                    .border(Color.gray)
                    .padding([.bottom], 5)
                       .foregroundColor(.secondary)
                       .cornerRadius(3)
                    .frame(width: 200, height: height)
    
                
    
                    Text(Posting)
                        .foregroundColor(.red)
                        .frame(width: 190)
                        .hidden()
                        .font(Font.custom("Helvetica Neue", size: 15))
                        .fixedSize(horizontal: false, vertical: true)
                        .background(GeometryReader { proxy in
                            Color.clear
                                .onChange(of: self.Posting, perform: { value in
                                    if proxy.size.height > height {
                                        height = proxy.size.height + 17.0
                                    }
                                })
                            
                        })
                
                
            }
        }
    }