Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to make collectioview show messages immediately #40

Closed
Sabellus opened this issue Dec 12, 2022 · 5 comments
Closed

How to make collectioview show messages immediately #40

Sabellus opened this issue Dec 12, 2022 · 5 comments
Assignees
Labels
question Further information is requested

Comments

@Sabellus
Copy link

What needs to be done so that when the chatviewcontroller push-navigates, the data is already ready?

An empty collection is shown. then messages? Like no spawn animation.

i played with it

collectionView.contentInsetAdjustmentBehavior = .always

changed to this

collectionView.contentInsetAdjustmentBehavior = .never

then the collection of elements is loaded immediately

but there are a number of artifacts associated with tabbar navigation and inputAccessoryView

inputAccessoryView twitches and disappears and reappears 2 times

I attach screenshots

IMG_7545
IMG_7546
IMG_7547

please help me how to do this, thanks

@ekazaev ekazaev self-assigned this Dec 12, 2022
@ekazaev ekazaev added the question Further information is requested label Dec 12, 2022
@ekazaev
Copy link
Owner

ekazaev commented Dec 12, 2022

@Sabellus Hi, thank you for your question.

First of all. If I correctly understood your description of inputAccessoryView behaviour. Apple broke the inputAccessoryView behaviour in IOS 16. So you can not use it anymore. If you compile your app with IOS 15 SDK it behves fine. I reported a bug and I know other people reported it, but it seems that Apple developers are not going to fix it anytime soon. You can find same jumpy behaviour in Facebook messenger for instance. So I would recommend to bring your input view to the actual view controller and attach it to keyboardLayoutGuide. You need a few hacks there but overall it works fine. Dont forget to change the keyboard inset setting code on keyboard presentation to:

    func keyboardWillChangeFrame(info: KeyboardInfo) {
        // ....
        let keyboardOffset = max(0, view.safeAreaLayoutGuide.layoutFrame.maxY - view.keyboardLayoutGuide.layoutFrame.minY)
        // ....
     }

In regard to message population as far as I remember push animation creates an extra animation loop so automatic inset value changes also happen with animation. As far as i remember thats why you need to populate collection and set thouse insets yourself at first time outside of that animation curve. So you want to do in your view controller something like:

    private var isFirsAppearance = true

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // Hack to prevent animation of the contentInset of first appearance. The same is used in MessageKit
        if isFirsAppearance {
            isFirsAppearance = false
            setupInsets()
            let initialCells = eventHandler.getInitialCells()
            if !initialCells.isEmpty {
                processUpdates(with: initialCells, animated: false, ignoreInterfaceActions: true)
            }
        }
    }

@Sabellus
Copy link
Author

What exactly should I do?

  1. remove
    override var inputAccessoryView: UIView? {
        inputBarView
    }

    override var canBecomeFirstResponder: Bool {
        true
    }
  1. adding and change
    view.addSubview(inputBarView)
    inputBarView.bottomAnchor.constraint(equalTo: self.view.keyboardLayoutGuide.topAnchor).isActive = true
    collectionView.bottomAnchor.constraint(equalTo: inputBarView.topAnchor, constant: 0),
  1. where to add this: let keyboardOffset = max(0, view.safeAreaLayoutGuide.layoutFrame.maxY - view.keyboardLayoutGuide.layoutFrame.minY)
func keyboardWillChangeFrame(info: KeyboardInfo) {
        guard !currentInterfaceActions.options.contains(.changingFrameSize),
              collectionView.contentInsetAdjustmentBehavior != .never,
              let keyboardFrame = collectionView.window?.convert(info.frameEnd, to: view),
              keyboardFrame.minY > 0,
              collectionView.convert(collectionView.bounds, to: collectionView.window).maxY > info.frameEnd.minY else {
            return
        }
        currentInterfaceActions.options.insert(.changingKeyboardFrame)
        let newBottomInset = collectionView.frame.minY + collectionView.frame.size.height - keyboardFrame.minY - collectionView.safeAreaInsets.bottom
        if newBottomInset > 0,
           collectionView.contentInset.bottom != newBottomInset {
            let positionSnapshot = chatLayout.getContentOffsetSnapshot(from: .bottom)

  1. setupInsets() what is there?
    eventHandler what is there?
     private var isFirsAppearance = true
     override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        // Hack to prevent animation of the contentInset of first appearance. The same is used in MessageKit
        if isFirsAppearance {
            isFirsAppearance = false
            setupInsets()
            let initialCells = eventHandler.getInitialCells()
            if !initialCells.isEmpty {
                processUpdates(with: initialCells, animated: false, ignoreInterfaceActions: true)
            }
        }
     }

@ekazaev
Copy link
Owner

ekazaev commented Dec 12, 2022

@Sabellus Yep you understand in general fine. But you should understand I am giving you a theoretical code. I can not write it for you. I can only give you some directions.

Keyboard appearance function will look someting like:

    func keyboardWillChangeFrame(info: KeyboardInfo) {
        guard !currentInterfaceActions.options.contains(.changingFrameSize),
              !currentInterfaceActions.options.contains(.showingPreview),
              !currentInterfaceActions.options.contains(.viewIsDisappearing),
              collectionView.contentInsetAdjustmentBehavior != .never else {
            return
        }

        currentInterfaceActions.options.insert(.changingKeyboardFrame)
        let keyboardAccessoryHeight = self.keyboardAccessoryView.bounds.height
        let keyboardOffset = max(0, view.safeAreaLayoutGuide.layoutFrame.maxY - view.keyboardLayoutGuide.layoutFrame.minY)
        let newBottomInset = keyboardOffset + keyboardAccessoryHeight

        if collectionView.contentInset.bottom != newBottomInset {
            let positionSnapshot = chatLayout.getContentOffsetSnapshot(from: .bottom)

            // Blocks possible updates when keyboard is being hidden interactively
            currentInterfaceActions.options.insert(.changingContentInsets)
            UIView.animate(withDuration: info.animationDuration, animations: { [weak self] in
                guard let self = self else { return }

                self.collectionView.performBatchUpdates({ [weak self] in
                    guard let self = self else { return }
                    self.collectionView.contentInset.bottom = newBottomInset
                    self.collectionView.verticalScrollIndicatorInsets.bottom = newBottomInset
                }, completion: nil)

                if let positionSnapshot = positionSnapshot, !self.isUserInitiatedScrolling {
                    self.chatLayout.restoreContentOffset(with: positionSnapshot)
                }
            }, completion: { [weak self] _ in
                self?.currentInterfaceActions.options.remove(.changingContentInsets)
            })
        }
    }

evenHandler in this case something that will return initial cells in the collection view
setupInsets looks something like

func setupInsets() {
        let insets = UIEdgeInsets(
            top: 0,
            left: 0,
            bottom: inputBarView.bounds.height - view.safeAreaInsets.bottom,
            right: 0
        )
        collectionView.contentInset = insets
        collectionView.scrollIndicatorInsets = insets
    }

I can not guarantee that is the only place you need changes. But I know that it is possible what you want to achieve.

@Sabellus
Copy link
Author

Appreciate your help! It works ❤

@ekazaev
Copy link
Owner

ekazaev commented Dec 12, 2022

You are very welcome @Sabellus. Best of luck

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants