ryanlintott / FrameUp

Reframing SwiftUI Views. A collection of tools to help with layout.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Great project. Text Justification SwiftUI issue.

waelsaad opened this issue · comments

commented

Hi Ryan :),

A few days ago I was excited when I find out about FrameUp, its exactly what I was looking for, however because of a specific SwiftUI bug with LazyVStack and a ScrollView it has prevented me from properly using FrameUp because of performance issues (not directly related to FrameUp).

I am hoping you will have some time to take a look please at my question on stackoverflow and see if you can help me with the appropriate solution for implementing simple Text justification for 100's of text blocks that are loaded dynamically.

I think implementing the text justification manually as described in my question on stackoverflow is the best approach from a performance perspective espcially when I need to have 100's of small Text views to be loading text dynamically. You'll understand what I mean when you have read my question here as to why I came to that conclusion -

https://stackoverflow.com/questions/76503942/swiftui-justify-text-by-words-without-using-uitextview-and-not-change-kerning

Thank you,

Wael

commented

Actually the bug explained here, is the root cause of the issue and it is preventing me from using FrameUp
https://stackoverflow.com/questions/66523786/swiftui-putting-a-lazyvstack-or-lazyhstack-in-a-scrollview-causes-stuttering-a

I wounder if there is a solution to this problem or a good workaround.

In my app I'd have to create 100s of the following view and if I put this code in a bigger for loop to loop within all of my text items and have it inside a ScrollView that contans a LazyVStack it will be slow to scroll. :(. I'll try to send a demo project sometime tomorrow.

    @ViewBuilder
    func justifiedItem(_ item: TextItem, _ language: LanguageType) -> some View {
        if let text = viewModel.getText(item, language) {
            let words = Array(text.string.split(separator: " ").map(String.init).enumerated())
            HFlowLayout(alignment: .justified) {
                ForEach(words, id: \.offset) { word in
                    Text(word.element)
                        .foregroundColor(viewModel.color(item))
                        .font(Font(viewModel.font(item, language)))
                }
            }
        }
    }
commented

My performance nightmare with the scrolling issue of 100's of these views was somewhat over last night by placing a LazyVStack around each of them. I will close this issue for now but will open another one for a different issue. Thank you.

    @ViewBuilder
    func justifiedItem(_ item: TextItem, _ language: LanguageType) -> some View {
        if let text = viewModel.getText(item, language) {
            let words = Array(text.string.split(separator: " ").map(String.init).enumerated())
            LazyVStack(spacing: 0) {
                HFlowLayout(alignment: .justified) {
                    ForEach(words, id: \.offset) { word in
                        Text(word.element)
                            .foregroundColor(viewModel.color(item))
                            .font(Font(viewModel.font(item, language)))
                            .multilineTextAlignment(textAlignment(item, language))
                            .frame(maxWidth: .infinity, alignment: frameAlignment(item, language))
                    }
                }
            }
        }
    }

As I mentioned in the other issue, the LazyVStack won't really affect the view as HFlowLayout is handling all the layout. It isn't lazy so you will have performance issues when laying out a large number of views.

Similar to your issue about breaking up longer words, if you want this kind of robust text handling it's best to use UIKit or CoreText. If you want to do it in SwiftUI my hacky version would also be more performant than HFlowLayout.