J'aimerais avoir une vue SwiftUI qui montre de nombreuses lignes de texte, avec les exigences suivantes:
On dirait que la solution la plus appropriée serait d'avoir une vue Liste, enveloppant UITextView/NSTextView natif.
Voici ce que j'ai jusqu'à présent. Il met en œuvre la plupart des exigences SAUF avoir la bonne hauteur pour les lignes.
//
// ListWithNativeTexts.Swift
// SUIToy
//
// Created by Jaanus Kase on 03.05.2020.
// Copyright © 2020 Jaanus Kase. All rights reserved.
//
import SwiftUI
let number = 20
struct ListWithNativeTexts: View {
var body: some View {
List(texts(count: number), id: \.self) { text in
NativeTextView(string: text)
}
}
}
struct ListWithNativeTexts_Previews: PreviewProvider {
static var previews: some View {
ListWithNativeTexts()
}
}
func texts(count: Int) -> [String] {
return (1...count).map {
(1...$0).reduce("Hello https://example.com:", { $0 + " " + String($1) })
}
}
#if os(iOS)
typealias NativeFont = UIFont
typealias NativeColor = UIColor
struct NativeTextView: UIViewRepresentable {
var string: String
func makeUIView(context: Context) -> UITextView {
let textView = UITextView()
textView.isEditable = false
textView.isScrollEnabled = false
textView.dataDetectorTypes = .link
textView.textContainerInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
textView.textContainer.lineFragmentPadding = 0
let attributed = attributedString(for: string)
textView.attributedText = attributed
return textView
}
func updateUIView(_ textView: UITextView, context: Context) {
}
}
#else
typealias NativeFont = NSFont
typealias NativeColor = NSColor
struct NativeTextView: NSViewRepresentable {
var string: String
func makeNSView(context: Context) -> NSTextView {
let textView = NSTextView()
textView.isEditable = false
textView.isAutomaticLinkDetectionEnabled = true
textView.isAutomaticDataDetectionEnabled = true
textView.textContainer?.lineFragmentPadding = 0
textView.backgroundColor = NSColor.clear
textView.textStorage?.append(attributedString(for: string))
textView.isEditable = true
textView.checkTextInDocument(nil) // make links clickable
textView.isEditable = false
return textView
}
func updateNSView(_ textView: NSTextView, context: Context) {
}
}
#endif
func attributedString(for string: String) -> NSAttributedString {
let attributedString = NSMutableAttributedString(string: string)
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = 4
let range = NSMakeRange(0, (string as NSString).length)
attributedString.addAttribute(.font, value: NativeFont.systemFont(ofSize: 24, weight: .regular), range: range)
attributedString.addAttribute(.foregroundColor, value: NativeColor.red, range: range)
attributedString.addAttribute(.backgroundColor, value: NativeColor.yellow, range: range)
attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: range)
return attributedString
}
Voici ce qu'il produit sur iOS. La sortie macOS est similaire.
Comment obtenir cette solution pour dimensionner les vues de texte avec des hauteurs correctes?
Une approche que j'ai essayée, mais non montrée ici, est de donner la hauteur "de l'extérieur vers l'intérieur" - pour spécifier la hauteur sur la ligne de liste elle-même avec cadre. Je peux calculer la hauteur d'un NSAttributedString lorsque je connais la largeur, que je peux obtenir avec geoReader. Cela fonctionne presque, mais est bogué et ne me semble pas bien, donc je ne le montre pas ici.
"Je voudrais avoir une vue SwiftUI qui montre plusieurs lignes de texte, ..."
Je crois que cela coche tous vos articles en ce qui concerne SwiftUI. MacOS, je vous laisse vous adapter. Votre texte peut être défini par programmation, de même que tous les styles différents que vous choisirez d'adopter. J'ai montré l'exemple aussi simplement que possible en utilisant des propriétés statiques. Vous pouvez rendre la plupart des cellules exploitables avec un NavigationLink
//
// ListWithNativeTextz.Swift
// CRecipes
//
// Created by Zahirudeen Premji on 5/7/20.
// With credit to Paul Hudson post
import SwiftUI
import SafariServices
struct ListWithNativeTextz: View {
let number = 20
let introText = "Hello "
let urlText = "https://hackingwithswift.com: "
var body: some View {
List(texts(count: number), id: \.self) { text in
NavigationLink(destination: NewDetail()) {
Text(self.introText)
.font(.headline)
.foregroundColor(.blue)
+ Text(self.urlText)
.font(.subheadline)
.foregroundColor(.green)
+ Text(text)
.font(.caption)
.foregroundColor(.red)
}
}
}
func texts(count: Int) -> [String] {
return (1...count).map {
(1...$0).reduce("", {$0 + " " + String($1)})
}
}
}
struct NewDetail: View {
// add a SafariView here to display the link in Safari
// You must import SafariServices and then follow instruction here :
// https://stackoverflow.com/questions/56518029/how-do-i-use-sfsafariviewcontroller-with-swiftui
// https://www.hackingwithswift.com/read/32/3/how-to-use-sfsafariviewcontroller-to-browse-a-web-page
var body: some View {
Text("Hello There")
}
}
struct ListWithNativeTextz_Previews: PreviewProvider {
static var newDetail = NewDetail()
static var previews: some View {
ListWithNativeTextz()
}
}
Branchez pour Paul Hudson dans le texte à: https://www.hackingwithswift.com/quick-start/swiftui/how-to-combine-text-views-together