如何使用 NSViewRepresentable 在 SwiftUI 视图中垂直居中 NSTextView?
How do I vertically center an NSTextView in a SwiftUI view with NSViewRepresentable?
我有一个简单的 NSViewRepresentable
,它包含一个 NSTextView
。
struct TextView: NSViewRepresentable {
typealias NSViewType = NSTextView
var text: NSAttributedString
func makeNSView(context: Context) -> NSTextView {
let view = NSTextView()
// set background color to show view bounds
view.backgroundColor = NSColor.systemBlue
view.drawsBackground = true
view.isEditable = false
view.isSelectable = false
return view
}
func updateNSView(_ nsView: NSTextView, context: Context) {
nsView.textStorage?.setAttributedString(text)
}
}
我想将它垂直居中,所以我使用了 VStack
和 Spacers
上下。
我想在它周围留下与 window 大小成比例的左右边距,所以我将它包裹在 GeometryReader
和 frame()
.
中
struct ContentView: View {
func textView() -> some View {
let text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
tempor incididunt ut labore et dolore magna aliqua.
"""
// Note: real application has more complex text than this, *not* using
// NSAttributedString isn't really an option, at least till SwiftUI has
// proper rich text support.
let size: CGFloat = 18
let font = NSFont(name: "LeagueSpartan-Bold", size: size)
let attrString = NSAttributedString(
string: text,
attributes: [ .foregroundColor: NSColor.systemYellow,
.font: font ?? NSFont.systemFont(ofSize: size) ])
return TextView(text: attrString)
}
var body: some View {
let textView: some View = self.textView()
return ZStack {
// use ZStack to provide window background color
Color(NSColor.systemTeal)
VStack {
Spacer()
GeometryReader { m2 in
textView.frame(width: m2.size.width / 1.618)
}
Spacer()
}
}
}
}
水平框架工作正常,但垂直间距根本不起作用:
(初始状态)
调整 window 的大小会产生恶作剧:
(从底部调整大小)
(从下到上调整大小,然后再向下调整)
哦,预览完全是香蕉:
如果我用 SwiftUI
本机 Text
替换我的自定义 TextView
,布局工作正常,这表明问题出在 TextView
。 (另请注意,默认 window 尺寸较小。)
NSTextView
的大小似乎设置不正确。如果我将 nsView.sizeToFit()
添加到 updateNSView()
:
func updateNSView(_ nsView: NSTextView, context: Context) {
nsView.textStorage?.setAttributedString(text)
nsView.sizeToFit()
}
这让我得到较小的默认 window 大小,并在我上下调整大小时阻止文本跳到 window 的底部,但文本仍然固定在顶部附近window 的预览仍然损坏,从底部调整大小仍然得到 NSTextView
暂时填充 window.
的大部分高度
我尝试摆弄的其他东西:isVerticallyResizable
、setContentCompressionResistancePriority
、autoresizingMask
和 translatesAutoresizingMaskIntoConstraints
、invalidateIntrinsicContentSize
。 None 这些似乎有任何明显的区别。
似乎我想要的是在包含的 SwiftUI 视图调整大小时更新 NSTextView
大小,但似乎没有任何明显的方法可以做到这一点,而且我无论如何都可能是错的。
如果你只需要显示 NSAttributedString
,据我了解,那么基于 NSTextField
的方法更合适,如下所示,因为 NSTextView
没有默认内部布局并需要明确的外部框架。
这里是修改可表示的,ContentView
不需要修改。
struct TextView: NSViewRepresentable {
typealias NSViewType = NSTextField
var text: NSAttributedString
func makeNSView(context: Context) -> NSTextField {
let view = NSTextField()
// set background color to show view bounds
view.backgroundColor = NSColor.systemBlue
view.drawsBackground = true
view.isEditable = false
view.isSelectable = false
view.lineBreakMode = .byWordWrapping
view.maximumNumberOfLines = 0
view.translatesAutoresizingMaskIntoConstraints = false
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.setContentCompressionResistancePriority(.required, for: .vertical)
return view
}
func updateNSView(_ nsView: NSTextField, context: Context) {
nsView.attributedStringValue = text
}
}
我有一个简单的 NSViewRepresentable
,它包含一个 NSTextView
。
struct TextView: NSViewRepresentable {
typealias NSViewType = NSTextView
var text: NSAttributedString
func makeNSView(context: Context) -> NSTextView {
let view = NSTextView()
// set background color to show view bounds
view.backgroundColor = NSColor.systemBlue
view.drawsBackground = true
view.isEditable = false
view.isSelectable = false
return view
}
func updateNSView(_ nsView: NSTextView, context: Context) {
nsView.textStorage?.setAttributedString(text)
}
}
我想将它垂直居中,所以我使用了 VStack
和 Spacers
上下。
我想在它周围留下与 window 大小成比例的左右边距,所以我将它包裹在 GeometryReader
和 frame()
.
struct ContentView: View {
func textView() -> some View {
let text = """
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod \
tempor incididunt ut labore et dolore magna aliqua.
"""
// Note: real application has more complex text than this, *not* using
// NSAttributedString isn't really an option, at least till SwiftUI has
// proper rich text support.
let size: CGFloat = 18
let font = NSFont(name: "LeagueSpartan-Bold", size: size)
let attrString = NSAttributedString(
string: text,
attributes: [ .foregroundColor: NSColor.systemYellow,
.font: font ?? NSFont.systemFont(ofSize: size) ])
return TextView(text: attrString)
}
var body: some View {
let textView: some View = self.textView()
return ZStack {
// use ZStack to provide window background color
Color(NSColor.systemTeal)
VStack {
Spacer()
GeometryReader { m2 in
textView.frame(width: m2.size.width / 1.618)
}
Spacer()
}
}
}
}
水平框架工作正常,但垂直间距根本不起作用:
(初始状态)
调整 window 的大小会产生恶作剧:
(从底部调整大小)
(从下到上调整大小,然后再向下调整)
哦,预览完全是香蕉:
如果我用 SwiftUI
本机 Text
替换我的自定义 TextView
,布局工作正常,这表明问题出在 TextView
。 (另请注意,默认 window 尺寸较小。)
NSTextView
的大小似乎设置不正确。如果我将 nsView.sizeToFit()
添加到 updateNSView()
:
func updateNSView(_ nsView: NSTextView, context: Context) {
nsView.textStorage?.setAttributedString(text)
nsView.sizeToFit()
}
这让我得到较小的默认 window 大小,并在我上下调整大小时阻止文本跳到 window 的底部,但文本仍然固定在顶部附近window 的预览仍然损坏,从底部调整大小仍然得到 NSTextView
暂时填充 window.
我尝试摆弄的其他东西:isVerticallyResizable
、setContentCompressionResistancePriority
、autoresizingMask
和 translatesAutoresizingMaskIntoConstraints
、invalidateIntrinsicContentSize
。 None 这些似乎有任何明显的区别。
似乎我想要的是在包含的 SwiftUI 视图调整大小时更新 NSTextView
大小,但似乎没有任何明显的方法可以做到这一点,而且我无论如何都可能是错的。
如果你只需要显示 NSAttributedString
,据我了解,那么基于 NSTextField
的方法更合适,如下所示,因为 NSTextView
没有默认内部布局并需要明确的外部框架。
这里是修改可表示的,ContentView
不需要修改。
struct TextView: NSViewRepresentable {
typealias NSViewType = NSTextField
var text: NSAttributedString
func makeNSView(context: Context) -> NSTextField {
let view = NSTextField()
// set background color to show view bounds
view.backgroundColor = NSColor.systemBlue
view.drawsBackground = true
view.isEditable = false
view.isSelectable = false
view.lineBreakMode = .byWordWrapping
view.maximumNumberOfLines = 0
view.translatesAutoresizingMaskIntoConstraints = false
view.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
view.setContentCompressionResistancePriority(.required, for: .vertical)
return view
}
func updateNSView(_ nsView: NSTextField, context: Context) {
nsView.attributedStringValue = text
}
}