我正在尝试通过
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
边框为绿色。
有什么原因会发生这种情况吗?
我设法通过覆盖
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)
])
}
}
我采用了@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)
])
}
}
我一直在做类似的事情。我正在 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
}
}