首页 > 解决方案 > 如何使用 NavigationLink 在 SwiftUI 中创建文本(仅文本中的一些单词)

问题描述

我正在为 iOS 开发 SwiftUI 应用程序。我想以这种方式格式化文本,其中蓝色单词应该是 NavigationLinks。文本的外观:

嵌入在文本段落中的蓝色链接

我知道可以将 UIKit 实现到 SwiftUI 代码中。但是,我不明白如何通过正常工作的 NavigationLinks 以这种方式使用 UIKit。

标签: swiftswiftuiswiftui-navigationlink

解决方案


使用这个 SO question作为基础,这是一个解决方案。左对齐,但一种方法。

import SwiftUI
struct DynamicLinkTextView: View {
    let text: String = "I want text like this, with NavigationLinks to another View. However, this doesn't work"
    let wordsWLinks: [String] = ["this", "View"]
    @State var selection: String?
    var textArray: [String]{
        text.components(separatedBy: " ")
    }
    var body: some View {
        NavigationView{
            VStack{
                MultilineHStack(textArray){ text in
                    VStack{
                        if wordsWLinks.contains(text.removePunctiation()){
                            NavigationLink(text + " ", destination: Text("link =  \(text)"), tag: text as String, selection: $selection)
                        }else{
                            Text(text + " ").fixedSize()
                        }
                    }
                }
            }
        }
    }
}
//https://stackoverflow.com/questions/57510093/swiftui-how-to-have-hstack-wrap-children-along-multiple-lines-like-a-collectio
public struct MultilineHStack: View {
    struct SizePreferenceKey: PreferenceKey {
        typealias Value = [CGSize]
        static var defaultValue: Value = []
        static func reduce(value: inout Value, nextValue: () -> Value) {
            value.append(contentsOf: nextValue())
        }
    }
    
    private let items: [AnyView]
    @State private var sizes: [CGSize] = []
    
    public init<Data: RandomAccessCollection,  Content: View>(_ data: Data, @ViewBuilder content: (Data.Element) -> Content) {
        self.items = data.map { AnyView(content($0)) }
    }
    
    public var body: some View {
        GeometryReader {geometry in
            ZStack(alignment: .topLeading) {
                ForEach(self.items.indices) { index in
                    self.items[index].background(self.backgroundView()).offset(self.getOffset(at: index, geometry: geometry))
                }
            }
        }.onPreferenceChange(SizePreferenceKey.self) {
            self.sizes = $0
        }
    }
    
    private func getOffset(at index: Int, geometry: GeometryProxy) -> CGSize {
        guard index < sizes.endIndex else {return .zero}
        let frame = sizes[index]
        var (x,y,maxHeight) = sizes[..<index].reduce((CGFloat.zero,CGFloat.zero,CGFloat.zero)) {
            var (x,y,maxHeight) = $0
            x += $1.width
            if x > geometry.size.width {
                x = $1.width
                y += maxHeight
                maxHeight = 0
            }
            maxHeight = max(maxHeight, $1.height)
            return (x,y,maxHeight)
        }
        if x + frame.width > geometry.size.width {
            x = 0
            y += maxHeight
        }
        return .init(width: x, height: y)
    }
    
    private func backgroundView() -> some View {
        GeometryReader { geometry in
            Rectangle()
                .fill(Color.clear)
                .preference(
                    key: SizePreferenceKey.self,
                    value: [geometry.frame(in: CoordinateSpace.global).size]
                )
        }
    }
}
struct DynamicLinkTextView_Previews: PreviewProvider {
    static var previews: some View {
        DynamicLinkTextView()
    }
}
extension String{
    func removePunctiation() -> String {
        self.trimmingCharacters(in: CharacterSet(charactersIn: ",."))
    }
}

推荐阅读