作为 UITextView inputAccessoryView 的 SwiftUI 视图具有不正确的框架

问题描述 投票:0回答:3

我正在尝试通过

UITextView
将 SwiftUI 视图作为附件视图添加到
UIHostingController
。以下是我的
UIInputView
配置

struct AccessoryView: View { /* SwiftUI View */ } 

class AccessoryInputView: UIInputView {

    private let controller: UIHostingController<AccessoryView>

    init(_ attachmentItem: Binding<AttachmentItemType>) {
        let parentView = AccessoryView(selectedAttachmentItem: attachmentItem)
        self.controller = UIHostingController<AccessoryView>(rootView: parentView)
        super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 44), inputViewStyle: .default)

        self.controller.view.translatesAutoresizingMaskIntoConstraints = false
        self.controller.view.backgroundColor = UIColor.clear
        self.addSubview(self.controller.view)

        self.layer.borderWidth = 2
        self.layer.borderColor = UIColor.blue.cgColor
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSLayoutConstraint.activate([
            self.widthAnchor.constraint(equalTo: self.controller.view.widthAnchor),
            self.heightAnchor.constraint(equalTo: self.controller.view.heightAnchor),
            self.centerXAnchor.constraint(equalTo: self.controller.view.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: self.controller.view.centerYAnchor)
        ])
    }
}

我用蓝色绘制了

UIInputView
的边框,并在
AccessoryView
周围设置了红色边框。正如您在屏幕截图中看到的,SwiftUI 视图偏移了几个点。
UITextView
边框为绿色。

有什么原因会发生这种情况吗?

ios swift uiview swiftui inputaccessoryview
3个回答
2
投票

我设法通过覆盖

AccessoryInputView
safeAreaInsets
来解决这个问题,如下所示:

struct AccessoryView: View { /* SwiftUI View */ } 

class AccessoryInputView: UIInputView {

    private let controller: UIHostingController<AccessoryView>

    init(_ attachmentItem: Binding<AttachmentItemType>) {
        let parentView = AccessoryView(selectedAttachmentItem: attachmentItem)
        self.controller = UIHostingController<AccessoryView>(rootView: parentView)
        super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 44), inputViewStyle: .default)

        self.controller.view.translatesAutoresizingMaskIntoConstraints = false
        self.controller.view.backgroundColor = UIColor.clear
        self.addSubview(self.controller.view)

        self.layer.borderWidth = 2
        self.layer.borderColor = UIColor.blue.cgColor
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    // HERE
    override var safeAreaInsets: UIEdgeInsets {
        .zero
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSLayoutConstraint.activate([
            self.widthAnchor.constraint(equalTo: self.controller.view.widthAnchor),
            self.heightAnchor.constraint(equalTo: self.controller.view.heightAnchor),
            self.centerXAnchor.constraint(equalTo: self.controller.view.centerXAnchor),
            self.centerYAnchor.constraint(equalTo: self.controller.view.centerYAnchor)
        ])
    }
}

0
投票

我采用了@wes的答案并使用泛型和构建器来使其更可重用:

本质上,我将

AccessoryView
结构体替换为
AccessoryContent
通用约束为 SwiftUI 视图,然后向
init
方法添加了一个构建器方法。

使用时,您只需要:

uiView.inputAccessoryView = AccessoryInputView {
  Text("Hello!")
}

AccessoryInputView.swift

class AccessoryInputView<AccessoryContent: View>: UIInputView {
    private let controller: UIHostingController<AccessoryContent>

    init(_ accessoryViewBuilder: () -> AccessoryContent ) {
        controller = UIHostingController<AccessoryContent>(rootView: accessoryViewBuilder())
        super.init(frame: CGRect(x: 0, y: 0, width: 0, height: 44), inputViewStyle: .default)

        controller.view.translatesAutoresizingMaskIntoConstraints = false
        controller.view.backgroundColor = UIColor.clear
        addSubview(controller.view)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override var safeAreaInsets: UIEdgeInsets {
        .zero
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        NSLayoutConstraint.activate([
            widthAnchor.constraint(equalTo: controller.view.widthAnchor),
            heightAnchor.constraint(equalTo: controller.view.heightAnchor),
            centerXAnchor.constraint(equalTo: controller.view.centerXAnchor),
            centerYAnchor.constraint(equalTo: controller.view.centerYAnchor)
        ])
    }
}

-1
投票

我一直在做类似的事情。我正在 SwiftUI 中完全构建一个 RTF 文档编辑器,我的输入附件视图就是这样完成的。我只是在定义或想要显示 inputAccessoryView 的任何地方调用此函数,就像在 makeUIView 中我有这一行“textView.setInputAccessory()”。下面的选择器都在我的扩展中定义。

extension UITextView {
internal func setInputAccessory()
{
    let activeLinkState = UserDefaults.standard.bool(forKey: "activeLink")
    let bar = UIToolbar()
    let textformat = UIBarButtonItem(image: UIImage(systemName: "textformat"), style: .plain, target: self, action: #selector(self.editView(sender:)))
    let linkformat = UIBarButtonItem(image: UIImage(systemName: "link"), style: .plain, target: self, action: #selector(self.linkView(sender:)))
    let bold = UIBarButtonItem(image: UIImage(systemName: "bold"), style: .plain, target: self, action: #selector(self.onBold(sender:)))
    let italic = UIBarButtonItem(image: UIImage(systemName: "italic"), style: .plain, target: self, action: #selector(self.onItalic(sender:)))
    let underline = UIBarButtonItem(image: UIImage(systemName: "underline"), style: .plain, target: nil, action: #selector(self.onUnderline(sender:)))
    let activeLink = activeLinkState ? UIBarButtonItem(image: UIImage(systemName: "link.circle.fill"), style: .plain, target: nil, action: #selector(self.toggleActiveLink(sender:))) : UIBarButtonItem(image: UIImage(systemName: "link.circle"), style: .plain, target: nil, action: #selector(self.toggleActiveLink(sender:)))
    let done = UIBarButtonItem(barButtonSystemItem: .done, target: self, action: #selector(self.doneButtonAction))
    let spacer = UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil)
    bar.items = [textformat, linkformat, spacer, bold, italic, underline, spacer, activeLink, done]
    bar.sizeToFit()
    self.inputAccessoryView = bar
}
}
© www.soinside.com 2019 - 2024. All rights reserved.