Search code examples
iosswiftuiviewswiftuiinputaccessoryview

SwiftUI view as a UITextView inputAccessoryView has incorrect frame


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?

ScreenShot


Solution

  • 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)
            ])
        }
    }