ios - SwiftUI 中自定义 UIViewRepresentable UITextView 的框架高度问题

标签 ios swiftui

我正在通过 UIViewRepresentable 为 SwiftUI 构建自定义 UITextView。它旨在显示 NSAttributedString ,并处理连杆压力机。一切正常,但是当我在 NavigationView 中显示此 View 时,框架高度完全困惑了。带有内联标题。

import SwiftUI

struct AttributedText: UIViewRepresentable {
  class Coordinator: NSObject, UITextViewDelegate {
    var parent: AttributedText

    init(_ view: AttributedText) {
      parent = view
    }

    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {
      parent.linkPressed(URL)
      return false
    }
  }

  let content: NSAttributedString
  @Binding var height: CGFloat
  var linkPressed: (URL) -> Void

  public func makeUIView(context: Context) -> UITextView {
    let textView = UITextView()
    textView.backgroundColor = .clear
    textView.isEditable = false
    textView.isUserInteractionEnabled = true
    textView.delegate = context.coordinator
    textView.isScrollEnabled = false
    textView.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
    textView.dataDetectorTypes = .link
    textView.textContainerInset = .zero
    textView.textContainer.lineFragmentPadding = 0
    return textView
  }

  public func updateUIView(_ view: UITextView, context: Context) {
    view.attributedText = content

    // Compute the desired height for the content
    let fixedWidth = view.frame.size.width
    let newSize = view.sizeThatFits(CGSize(width: fixedWidth, height: CGFloat.greatestFiniteMagnitude))

    DispatchQueue.main.async {
      self.height = newSize.height
    }
  }

  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
}


struct ContentView: View {

  private var text: NSAttributedString {
    NSAttributedString(string: "Eartheart is the principal settlement for the Gold Dwarves in East Rift and it is still the cultural and spiritual center for its people. Dwarves take on pilgrimages to behold the great holy city and take their trips from other countries and the deeps to reach their goal, it use to house great temples and shrines to all the Dwarven pantheon and dwarf heroes but after the great collapse much was lost.\n\nThe lords of their old homes relocated here as well the Deep Lords. The old ways of the Deep Lords are still the same as they use intermediaries and masking themselves to undermine the attempts of assassins or drow infiltrators. The Gold Dwarves outnumber every other race in the city and therefor have full control of the city and it's communities.")
  }

  @State private var height: CGFloat = .zero

  var body: some View {
    NavigationView {
      List {
        AttributedText(content: text, height: $height, linkPressed: { url in print(url) })
          .frame(height: height)

        Text("Hello world")
      }
      .listStyle(GroupedListStyle())
      .navigationBarTitle(Text("Content"), displayMode: .inline)
    }
  }
}

struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}

运行此代码时,您将看到 AttributedText单元格将太小而无法容纳其内容。

enter image description here

当您删除 displayMode: .inline来自 navigationBarTitle 的参数,它显示得很好。

enter image description here

但是,如果我添加另一行来显示高度值( Text("\(height)") ),它会再次中断。

enter image description here

也许这是某种通过状态更改的 View 更新触发的竞争条件? height value 本身是正确的,只是框架实际上并没有那么高。有解决方法吗?

使用 ScrollViewVStack确实解决了问题,但我真的更喜欢使用 List由于内容在真实应用程序中的显示方式。

最佳答案

如果您不更改文本,即使没有绑定(bind),您也可以计算宽度和高度并将它们用作框架。

List {
        // you don't need binding height
        AttributedText(content: text, linkPressed: { url in print(url) })
          .frame(height: frameSize(for: text).height)

        Text("Hello world")
      }
func frameSize(for text: String, maxWidth: CGFloat? = nil, maxHeight: CGFloat? = nil) -> CGSize {
        let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.preferredFont(forTextStyle: .body)
        ]
        let attributedText = NSAttributedString(string: text, attributes: attributes)
        let width = maxWidth != nil ? min(maxWidth!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
        let height = maxHeight != nil ? min(maxHeight!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
        let constraintBox = CGSize(width: width, height: height)
        let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral
        return rect.size
    }
带分机号:
extension String {
    func frameSize(maxWidth: CGFloat? = nil, maxHeight: CGFloat? = nil) -> CGSize {
        let attributes: [NSAttributedString.Key: Any] = [
            .font: UIFont.preferredFont(forTextStyle: .body)
        ]
        let attributedText = NSAttributedString(string: self, attributes: attributes)
        let width = maxWidth != nil ? min(maxWidth!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
        let height = maxHeight != nil ? min(maxHeight!, CGFloat.greatestFiniteMagnitude) : CGFloat.greatestFiniteMagnitude
        let constraintBox = CGSize(width: width, height: height)
        let rect = attributedText.boundingRect(with: constraintBox, options: [.usesLineFragmentOrigin, .usesFontLeading], context: nil).integral
        return rect.size
    }
}

关于ios - SwiftUI 中自定义 UIViewRepresentable UITextView 的框架高度问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60437014/

相关文章:

SwiftUI:NavigationButton 初始值设定项给出错误

SwiftUI:删除所有核心数据实体条目后列表不会自动更新

ios - [iOS]用户手动杀掉应用后接收推送通知的处理

ios - 如何在 iOS 10 中设置应用程序图标角标(Badge)编号?

ios - IBDesignable 在添加到 UIStackView 时不起作用

ios - 使用 Coregraphics 一次绘制多个 Guage 图表

SwiftUI:绑定(bind)从环境对象派生的结构的属性

swift - 如何在 WidgetKit iOS14 中刷新多个计时器?

ios - 拒绝 Codesign 访问钥匙串(keychain)后,Xcode 不允许为设备构建

swiftui - 在SwiftUI中打开暗模式时更改背景颜色