如何解决为什么 macOS 中的 SwiftUI 多行换行文本在预览版中有效,而在实际应用中无效?
import SwiftUI
#if canImport(UIKit)
typealias AppColor = UIColor
#else
typealias AppColor = NSColor
#endif
struct ReportErrorUI: View {
enum Order {
case even,odd
}
let error: String
let order: Order
init(error: String,order: Order) {
self.error = error
self.order = order
}
var body: some SwiftUI.View {
let bgColor = (order == .even ? AppColor.systemGreen : .systemRed).withAlphaComponent(0.5)
return vstack(alignment: .leading,spacing: 0,content: {
HStack(alignment: .center,spacing: 8,content: {
Image("icon.error")
Text(error).font(.body)
}).padding(8)
Color(.magenta).frame(height: 2)
}).background(Color(bgColor))
}
}
现在我可以预览了:
import SwiftUI
@available(OSX 11.0,*)
struct ReportErrorUI_Previews: PreviewProvider {
static let shortText = "Etiam habebis sem dicantur magna mollis euismod."
static let longText = "Tityre,tu patulae recubans sub tegmine fagi dolor. Idque Caesaris facere voluntate liceret: sese habere. Unam incolunt Belgae,aliam Aquitani,tertiam."
static var previews: some SwiftUI.View {
Group {
vstack(spacing: 0) {
ReportErrorUI(error: longText,order: .even)
ReportErrorUI(error: shortText,order: .odd)
}.previewLayout(.sizeThatFits)
.background(Color.white).padding().frame(width: 320)
}
}
}
它看起来像:
如您所见,Text
视图按预期工作 - 长文本已换行。
现在我将这个 ReportErrorUI
与老式的 NSTextField
多行标签一起添加到现有的 AppKit 应用程序中,通过 NSHostingController
或 NSHostingView
。两个 UI 元素都添加到 nsstackview
中。
class MainViewController: ReusableViewController {
private lazy var stackView = nsstackview()
override func setupUI() {
view.addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: view.topAnchor,constant: 8).isActive = true
stackView.bottomAnchor.constraint(equalTo: view.bottomAnchor,constant: -8).isActive = true
stackView.leadingAnchor.constraint(equalTo: view.leadingAnchor,constant: 8).isActive = true
stackView.trailingAnchor.constraint(equalTo: view.trailingAnchor,constant: -8).isActive = true
stackView.widthAnchor.constraint(greaterThanorEqualToConstant: 120).isActive = true
stackView.heightAnchor.constraint(greaterThanorEqualToConstant: 80).isActive = true
stackView.orientation = .vertical
stackView.spacing = 8
stackView.distribution = .fill
stackView.alignment = .leading
// Wrappable multiline label. See: https://stackoverflow.com/a/35920653/1418981
let label = NSTextField()
label.stringValue = "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum."
label.setContentHuggingPriority(.init(1),for: .horizontal)
label.setContentCompressionResistancePriority(.init(1),for: .horizontal)
label.isEditable = false
label.backgroundColor = NSColor.magenta.withAlphaComponent(0.4)
label.isBezeled = false
label.isSelectable = true
//> Below 4 lines seems not needed.
// label.usesSingleLineMode = false
// label.lineBreakMode = .byWordWrapping
// label.cell?.wraps = true
// label.cell?.isScrollable = false
//<
stackView.addArrangedSubview(label)
do {
let ui = ReportErrorUI(error: "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum.",order: .even)
let view = NSHostingView(rootView: ui)
view.setContentHuggingPriority(.init(1),for: .horizontal)
view.setContentCompressionResistancePriority(.init(1),for: .horizontal)
view.setContentCompressionResistancePriority(.required,for: .vertical)
stackView.addArrangedSubview(view)
}
do {
let ui = ReportErrorUI(error: "Excepteur sint obcaecat cupiditat non proident culpa. A communi observantia non est recedendum.",order: .odd)
let vc = NSHostingController(rootView: ui)
addChild(vc)
vc.view.setContentHuggingPriority(.init(1),for: .horizontal)
vc.view.setContentCompressionResistancePriority(.init(1),for: .horizontal)
vc.view.setContentCompressionResistancePriority(.required,for: .vertical)
stackView.addArrangedSubview(vc.view)
}
do {
let view = ReusableView()
view.backgroundColor = .yellow
view.heightAnchor.constraint(greaterThanorEqualToConstant: 12).isActive = true
stackView.addArrangedSubview(view)
}
}
}
结果老式的 NSTextField
按预期工作 - 多行文本在应用程序窗口调整大小时自动换行。但 SwiftUI Text
视图中的文本未换行。
来自 UIKit 世界的技巧,例如 lineLimit(nil)
或 .fixedSize(horizontal: false,vertical: true)
,要么不起作用,要么破坏应用程序窗口布局。
例如,.fixedSize(horizontal: false,vertical: true)
的工作原理 - 应用程序窗口布局已损坏。应用程序窗口不能垂直调整大小。
在 setContentCompressionResistancePriority
或 setContentHuggingPriority
上设置的 horizontal
、vertical
轴的 NSHostingView
或 NSHostingController
的任意组合无济于事。
如何使多行包装 SwiftUI Text
在 macOS 上正常工作?
解决方法
macOS 的解决方案。
感谢这篇文章:https://zalogatomek.medium.com/swiftui-missing-intrinsic-content-size-how-to-get-it-6eca8178a71f
struct IntrinsicContentSizePreferenceKey: PreferenceKey {
static let defaultValue: CGSize = .zero
static func reduce(value: inout CGSize,nextValue: () -> CGSize) {
value = nextValue()
}
}
extension View {
func readIntrinsicContentSize(to size: Binding<CGSize>) -> some View {
background(GeometryReader { proxy in
Color.clear.preference(
key: IntrinsicContentSizePreferenceKey.self,value: proxy.size
)
})
.onPreferenceChange(IntrinsicContentSizePreferenceKey.self) {
size.wrappedValue = $0
}
}
}
struct MultilineText: View {
@State private var textSize: CGSize = .zero
let text: String
init(_ text: String) {
self.text = text
}
var body: some SwiftUI.View {
Text(text).readIntrinsicContentSize(to: $textSize).fixedSize(horizontal: false,vertical: true).frame(height: textSize.height)
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。