使用 ViewBuilder 时内容包装未对齐的 HStack

时间:2021-05-07 09:14:39

标签: swiftui swiftui-navigationlink

我从 another question 获得以下代码并尝试在方法 ViewBuilder 上使用 func text(for word: String) -> some View 以将一些 HStack 文本内容包装到 NavigationLink 中。< /p>

问题是,一旦我这样做,计算出的对齐就会被破坏。

如果从命名方法中删除 @ViewBuilder 注释,内容将正确对齐。

我该如何解决这个问题?

public struct HStackTextWrapping: View {
    
    
    private let words: [String]
    private let parentView: ParentView
    private let textModifiers: (Text) -> Text
    
    @State private var totalHeight: CGFloat
    
    public init(_ string: String,
         in parentView: ParentView,
         @ViewBuilder textModifiers: @escaping (Text) -> Text = { $0 })
    {
        self.words = string.components(separatedBy: .whitespaces)
        self.parentView = parentView
        self.textModifiers = textModifiers
        
        switch parentView {
        case .scrollview:
            totalHeight = .zero
        case .vstack:
            totalHeight = .infinity
        }
    }
    
    public var body: some View {
        VStack {
            GeometryReader { geometry in
                self.generateContent(in: geometry)
            }
        }
        .ifCondition(self.parentView == .scrollview) {
            $0.frame(height: totalHeight)
        }
        .ifCondition(self.parentView == .vstack) {
            $0.frame(maxHeight: totalHeight)
        }
    }
    
    private func generateContent(in g: GeometryProxy) -> some View {
        var width: CGFloat = .zero
        var height: CGFloat = .zero
        
        return ZStack(alignment: .topLeading) {
            ForEach(self.words, id: \.self) { word in
                self.text(for: word)
                    .padding(0)
                    .alignmentGuide(.leading) { d in
                        if abs(width - d.width) > g.size.width {
                            width = 0
                            height -= d.height
                        }
                        let result = width
                        if word == self.words.last {
                            width = 0
                        } else {
                            width -= d.width
                        }
                        print("(\(result), ")
                        return result
                    }
                    .alignmentGuide(.top) { d in
                        let result = height
                        if word == self.words.last {
                            height = 0
                        }
                        print("\(result)) " + word)
                        return result
                    }
            }
        }
        .background(viewHeightReader($totalHeight))
        
    }
    
    @ViewBuilder
    private func text(for word: String) -> some View {
        let potentialWhitespace = word == self.words.last ? "" : " "
        let text = self.textModifiers(Text(word + potentialWhitespace))
        
        if word.first == "@" {
            NavigationLink(destination: AnyView(ProfileView(username: String(word.dropFirst()))), label: { text })
                .foregroundColor(ColorPalette.primary.color)
        }
        else if word.first == "#" {
            NavigationLink(destination: AnyView(Text(word)), label: { text })
                .foregroundColor(ColorPalette.primary.color)
        }
        else {
            text
        }
    }
    
    private func viewHeightReader(_ binding: Binding<CGFloat>) -> some View {
        return GeometryReader { geometry -> Color in
            let rect = geometry.frame(in: .local)
            DispatchQueue.main.async {
                binding.wrappedValue  = rect.size.height
            }
            return .clear
        }
    }
    
    private func resolveTags(in text: String) {
        
    }
}

public enum ParentView {
    case scrollview
    case vstack
}

0 个答案:

没有答案