微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么 macOS 中的 SwiftUI 多行换行文本在预览版中有效,而在实际应用中无效?

如何解决为什么 macOS 中的 SwiftUI 多行换行文本在预览版中有效,而在实际应用中无效?

我有一个显示图像和文本的 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 视图按预期工作 - 长文本已换行。

Swift UI Preview

现在我将这个 ReportErrorUI 与老式的 NSTextField 多行标签一起添加到现有的 AppKit 应用程序中,通过 NSHostingControllerNSHostingView。两个 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 视图中的文本未换行。

Real App

来自 UIKit 世界的技巧,例如 lineLimit(nil).fixedSize(horizontal: false,vertical: true),要么不起作用,要么破坏应用程序窗口布局。

例如,.fixedSize(horizontal: false,vertical: true) 的工作原理 - 应用程序窗口布局已损坏。应用程序窗口不能垂直调整大小。

Broken layout

setContentCompressionResistancePrioritysetContentHuggingPriority 上设置的 horizontalvertical 轴的 NSHostingViewNSHostingController 的任意组合无济于事。

如何使多行包装 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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?