I am trying to add a SwiftUI view as an accessory view to a UITextView through a UIHostingController. Following is my UIInputView configuration
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)
])
}
}
I have drawn the border of the UIInputView in blue and the have set a border around the AccessoryView in red. As you can see in the screenshot the SwiftUI view is offset by couple points. UITextView has the borders in green.
Any reason why this happens?

I managed to fix this by overriding AccessoryInputView's safeAreaInsets like this:
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)
])
}
}
I took @wes's answer and used generics and a builder to make it more reusable:
In essence, I replaced the AccessoryView struct with an AccessoryContent generic constrained to be a SwiftUI View and then aded a builder method to the init method.
To use, all you need is:
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)
])
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With