Add a mouse hover effect / UIPointer Interaction to any iOS or iPadOS app views.

As Apple describes Pointer Interactions in its support document:

Pointer Interactions: Support pointer interactions in your custom controls and views.

iPadOS 13.4 introduces dynamic pointer effects and behaviours that enhance the experience of using an iPad with an external input device, like a trackpad or mouse. As people use an input device, iPadOS automatically adapts the pointer to the current context, providing rich visual feedback and the precision needed to enhance productivity and simplify everyday tasks.

Note:
UIPointer Interaction is only related to the iPad / iPadOS app

Initial Step:
Add ‘UIPointerInteractionDelegate’ in your View Controller’s Class Declaration

For UIButton:

Out-of-Box Implementation:

button.isPointerInteractionEnabled = true

UIView and Custom Implementation for UIButton:

if #available(iOS 13.4, *) {
  customPointerInteraction(on: myButton, pointerInteractionDelegate: self)
}
// MARK: - UIPointerInteractionDelegate 
@available(iOS 13.4, *)
func customPointerInteraction(on view: UIView, pointerInteractionDelegate:  
UIPointerInteractionDelegate){ 
  let pointerInteraction = UIPointerInteraction(delegate:  
pointerInteractionDelegate) 
  view.addInteraction(pointerInteraction) 
}

@available(iOS 13.4, *) 
func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: 
UIPointerRegion) -> UIPointerStyle {
  var pointerStyle: UIPointerStyle? 
 
  if let interactionView = interaction.view {
      let targetedPreview = UITargetedPreview(view: interactionView)
      pointerStyle = UIPointerStyle(effect:
UIPointerEffect.highlight(targetedPreview))
  }
  return pointerStyle
}

For UITableViewCell:

Add the following code to your UITableViewController’s File

// MARK: - UIPointerInteractionDelegate

    @available(iOS 13.4, *)
    func customPointerInteraction(on view: UITableViewCell, pointerInteractionDelegate: UIPointerInteractionDelegate){
        let pointerInteraction = UIPointerInteraction(delegate: pointerInteractionDelegate)
        view.addInteraction(pointerInteraction)
    }
     
    @available(iOS 13.4, *)
    func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
        var pointerStyle: UIPointerStyle?
        print("pointerInteraction view", interaction.view!)
        if let interactionView = interaction.view {
            let targetedPreview = UITargetedPreview(view: interactionView)
            pointerStyle = UIPointerStyle(effect: UIPointerEffect.hover(targetedPreview, preferredTintMode: .overlay, prefersShadow: true, prefersScaledContent: true))
        }
        return pointerStyle
    }
    
    @available(iOS 13.4, *)
        func pointerInteraction(_ interaction: UIPointerInteraction, willEnter region: UIPointerRegion, animator: UIPointerInteractionAnimating) {
        if let interactionView = interaction.view {
            animator.addAnimations {
                interactionView.alpha = 0.5
            }
        }
    }

    @available(iOS 13.4, *)
    func pointerInteraction(_ interaction: UIPointerInteraction, willExit region: UIPointerRegion, animator: UIPointerInteractionAnimating) {
        if let interactionView = interaction.view {
            animator.addAnimations {
                interactionView.alpha = 1.0
            }
        }
    }

Now Add this code to the ‘UITableView cellForRowAt indexPath’ Code Block:

override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath)

        // Configure the cell...
        
        if #available(iOS 13.4, *) {
            customPointerInteraction(on: cell.self, pointerInteractionDelegate: self)
        }

    return cell

    }

For UICollectionView:

Add the following code to your UICollectionViewController’s File

// MARK: - UIPointerInteractionDelegate

    @available(iOS 13.4, *)
    func customPointerInteraction(on view: UICollectionViewCell, pointerInteractionDelegate: UIPointerInteractionDelegate){
        let pointerInteraction = UIPointerInteraction(delegate: pointerInteractionDelegate)
        view.addInteraction(pointerInteraction)
    }
     
    @available(iOS 13.4, *)
    func pointerInteraction(_ interaction: UIPointerInteraction, styleFor region: UIPointerRegion) -> UIPointerStyle? {
        var pointerStyle: UIPointerStyle?
        print("pointerInteraction view", interaction.view!)
        if let interactionView = interaction.view {
            let targetedPreview = UITargetedPreview(view: interactionView)
            pointerStyle = UIPointerStyle(effect: UIPointerEffect.hover(targetedPreview, preferredTintMode: .overlay, prefersShadow: true, prefersScaledContent: true))
        }
        return pointerStyle
    }
    
    @available(iOS 13.4, *)
        func pointerInteraction(_ interaction: UIPointerInteraction, willEnter region: UIPointerRegion, animator: UIPointerInteractionAnimating) {
        if let interactionView = interaction.view {
            animator.addAnimations {
                interactionView.alpha = 0.5
            }
        }
    }

    @available(iOS 13.4, *)
    func pointerInteraction(_ interaction: UIPointerInteraction, willExit region: UIPointerRegion, animator: UIPointerInteractionAnimating) {
        if let interactionView = interaction.view {
            animator.addAnimations {
                interactionView.alpha = 1.0
            }
        }
    }

Now Add this code to the ‘UICollectionView cellForItemAt indexPath’ Code Block:

override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        let cell = appIconCollectionView.dequeueReusableCell(withReuseIdentifier: reuseIdentifier, for: indexPath) as! yourCollectionViewCell
        
        if #available(iOS 13.4, *) {
            customPointerInteraction(on: cell.self, pointerInteractionDelegate: self)
        }

    return cell

    }

I hope this information helps you get the desired effect…

Thanks & Regards
Mandar Apte

Published by Mandar Apte

Mandar is a Mumbai-based multi-disciplinary designer with UX/UI, Logo, Symbol, and Brand Identity design expertise. He currently runs his Mudrkashar Linguistic Apple iPhone, iPad, and Mac app business in the heart of Mumbai city.

Leave a comment

Leave a Reply