Как я могу создать текст в SwiftUI с помощью NavigationLink (только некоторые слова из текста)

Я работаю над приложением SwiftUI для iOS. Я хочу отформатировать текст таким образом, где синие слова должны быть NavigationLinks. Как должен выглядеть текст:

Синие ссылки, встроенные в абзац текста

Я знаю, что можно внедрить UIKit в код SwiftUI. Однако я не понимаю, как я могу использовать UIKit таким образом с нормально работающими ссылками NavigationLinks.


person Dmitrii    schedule 15.05.2021    source источник
comment
Я не уверен, что это возможно. См. здесь   -  person George_E    schedule 15.05.2021
comment
Строка статична? Если это так, вы можете создать несколько текстовых представлений, и синие будут иметь .onTapGesture. Чтобы сделать его динамичным, потребуется немного больше работы / размышлений.   -  person lorem ipsum    schedule 15.05.2021


Ответы (1)


Используя этот вопрос SO в качестве основы это решение. Слева оправдан но подход.

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: ",."))
    }
}
person lorem ipsum    schedule 15.05.2021
comment
Спасибо! Это действительно полезно! Теперь у меня проблема, потому что, когда я встраиваю View, как это, в VStack, SwiftUI уверен, что высота этого представления составляет одну строку (когда это действительно количество текстовых строк. Но я уверен, что собираюсь решить эту проблему. - person Dmitrii; 16.05.2021