inputAccessoryView sizing problem on iPhones without physical home button

63 Views Asked by At

inputAccessoryView's background view is falling under its own textField and profile picture imageView.

It works fine on regular screen iPhones, but on new iPhones with notches it looks like this:

enter image description here

Here's how it looks animated when keyboard appears: Transition animation on becomeFirstResponder()

Here's my tableView in which I'm trying to add accessoryView:

import UIKit
import SDWebImage

class CommentsTableViewController: UITableViewController {
    
    let viewModel = CommentsViewModel()
    
    let postID: String
    let postCaption: String
    let postDate: Date
    let postAuthor: ZoogramUser
    
    var keyboardAccessoryView: CommentAccessoryView = {
        let commentAccessoryView = CommentAccessoryView()
        return commentAccessoryView
    }()
    
    init(post: UserPost) {
        self.postID = post.postID
        self.postCaption = post.caption
        self.postDate = post.postedDate
        self.postAuthor = post.author
        super.init(style: .grouped)
        self.tableView.register(PostCommentsTableViewCell.self, forCellReuseIdentifier: PostCommentsTableViewCell.identifier)
        
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.title = "Comments"
        
        keyboardAccessoryView.delegate = self
        configureKeyboardAccessoryView()
        viewModel.getComments(for: self.postID) {
            self.tableView.reloadData()
        }
        
        tableView.backgroundColor = .systemBackground
        tableView.keyboardDismissMode = .interactive
        tableView.rowHeight = UITableView.automaticDimension
        tableView.estimatedRowHeight = 100
        tableView.allowsSelection = false
        tableView.separatorStyle = .none
    }
    
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        becomeFirstResponder()
    }
    
    override var inputAccessoryView: UIView? {
        keyboardAccessoryView.heightAnchor.constraint(greaterThanOrEqualToConstant: 60).isActive = true
        keyboardAccessoryView.backgroundColor = .systemOrange
        return keyboardAccessoryView
    }
    
    override var canBecomeFirstResponder: Bool {
        return true
    }
    
    
    func configureKeyboardAccessoryView() {
        guard let photoURL = AuthenticationManager.shared.getCurrentUserProfilePhotoURL() else {
            return
        }
        keyboardAccessoryView.userProfilePicture.sd_setImage(with: photoURL)
    }
}

And here's code for my CommentAccessoryView which I use to override inputAccessoryView:

import UIKit

protocol CommentAccessoryViewProtocol {
    func postButtonTapped(commentText: String)
}

class CommentAccessoryView: UIView {
    
    var delegate: CommentAccessoryViewProtocol?
    
    var userProfilePicture: UIImageView = {
        let imageView = UIImageView()
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView.contentMode = .scaleAspectFit
        imageView.clipsToBounds = true
        imageView.backgroundColor = .secondarySystemBackground
        imageView.contentMode = .scaleAspectFill
        return imageView
    }()
    
    var commentTextField: AccessoryViewTextField = {
        let textField = AccessoryViewTextField()
        textField.translatesAutoresizingMaskIntoConstraints = false
        textField.backgroundColor = .systemBackground
        textField.placeholder = "Enter comment"
        textField.clipsToBounds = true
        textField.layer.borderWidth = 1
        textField.layer.borderColor = UIColor.placeholderText.cgColor
        return textField
    }()
    
    var postButton: UIButton = {
        let button = UIButton(type: .system)
        button.translatesAutoresizingMaskIntoConstraints = false
        button.widthAnchor.constraint(equalToConstant: 30).isActive = true
        button.heightAnchor.constraint(equalToConstant: 30).isActive = true
        button.clipsToBounds = true
        button.layer.cornerRadius = 30/2
        button.setImage(UIImage(systemName: "arrow.up.circle.fill", withConfiguration: UIImage.SymbolConfiguration(pointSize: 35)), for: .normal)
        button.tintColor = .systemBlue
        button.addTarget(self, action: #selector(didTapPostButton), for: .touchUpInside)
        return button
    }()
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupConstraints()
        backgroundColor = .systemBackground
        commentTextField.rightView = postButton
        commentTextField.rightViewMode = .always
        autoresizingMask = .flexibleHeight
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        
        setViewCornerRadius()
    }
    
    func setViewCornerRadius() {
        userProfilePicture.layer.cornerRadius = userProfilePicture.frame.height / 2
        commentTextField.layer.cornerRadius = commentTextField.frame.height / 2
    }
    func setupConstraints() {
        self.addSubviews(userProfilePicture, commentTextField)
        
        NSLayoutConstraint.activate([
            
            userProfilePicture.leadingAnchor.constraint(equalTo: self.leadingAnchor, constant: 10),
            userProfilePicture.centerYAnchor.constraint(equalTo: self.safeAreaLayoutGuide.centerYAnchor),
            userProfilePicture.widthAnchor.constraint(equalToConstant: 40),
            userProfilePicture.heightAnchor.constraint(equalToConstant: 40),
            
            commentTextField.leadingAnchor.constraint(equalTo: userProfilePicture.trailingAnchor, constant: 10),
            commentTextField.centerYAnchor.constraint(equalTo: userProfilePicture.centerYAnchor),
            commentTextField.heightAnchor.constraint(equalToConstant: 40),
            commentTextField.trailingAnchor.constraint(equalTo: self.trailingAnchor, constant: -10),
            
        ])
    }
    
    override var intrinsicContentSize: CGSize {
        return CGSize.zero
    }
    
    @objc func didTapPostButton() {
        guard let text = commentTextField.text else {
            return
        }
        commentTextField.resignFirstResponder()
        delegate?.postButtonTapped(commentText: text)
    }
}

I've spent days trying to google a fix for that but nothing helps.

There were posts saying they were able to fix something similar by setting customView's bottom constraint to a safe area with the following method:

        override func didMoveToWindow() {
            if #available(iOS 11.0, *) {
                if let window = window {
                    let bottomAnchor = bottomAnchor.constraint(lessThanOrEqualToSystemSpacingBelow: window.safeAreaLayoutGuide.bottomAnchor, multiplier: 1.0)
                    bottomAnchor.isActive = true
                }
            }
        }

But when I use it, AutoLayout starts complaining.

UPDATE: I did what HangarRash recommended, changed CommentAccessoryView from UIView to UIInputView and centering profileImageView and textField to view itself and not to safe area. Now it's a little bit better, but seems to ignore safe area, inputAccessoryView should be above Home indicator but lies beneath it instead. Looking at last cell in TableView and Scroll indicator, it seems like TableView also isn't aware of inputAccessoryView and goes under it.

enter image description here

0

There are 0 best solutions below