Trying to use UIScrollEdgeElementContainerInteraction

I have a UIKit app with a custom navigation controller. I want my view title to go up into the navigation bar when the user scrolls down the screen. It looks like UIScrollEdgeElementContainerInteraction should do what I want, but I am having trouble using it.

Below is a sample, where a header view represents a title. I added the interaction to the header view, but it seems to have no effect. Am I missing a step? Perhaps I misunderstand what this is supposed to do, or perhaps I do not understand the preconditions to make this work.

I am hoping someone can tell me what I am doing wrong, or point me to some working sample code.

Thank you.

John

class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    var headerView: UIVisualEffectView!
    var tableView: UITableView!
    var interaction: UIScrollEdgeElementContainerInteraction!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        self.tableView = UITableView()
        self.tableView.translatesAutoresizingMaskIntoConstraints = false
        self.tableView.topEdgeEffect.style = .soft
        self.tableView.delegate = self
        self.tableView.dataSource = self
        self.tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
        
        self.view.addSubview(self.tableView)
        self.view.addConstraints([
            self.tableView.topAnchor.constraint(equalTo: self.view.topAnchor),
            self.tableView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            self.tableView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            self.tableView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
        ])
        
        
        self.headerView = UIVisualEffectView(effect: UIGlassEffect(style: .regular))
        self.headerView.translatesAutoresizingMaskIntoConstraints = false
        self.headerView.backgroundColor = .green
        self.view.addSubview(self.headerView)
        self.view.addConstraints([
            self.headerView.topAnchor.constraint(equalTo: self.view.topAnchor),
            self.headerView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
            self.headerView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor),
            self.headerView.heightAnchor.constraint(equalToConstant: 100.0),
        ])
        let label = UILabel()
        label.translatesAutoresizingMaskIntoConstraints = false
        label.text = "my text"
        self.headerView.contentView.addSubview(label)
        self.headerView.contentView.addConstraints([
            label.centerXAnchor.constraint(equalTo: self.headerView.contentView.centerXAnchor),
            label.centerYAnchor.constraint(equalTo: self.headerView.contentView.centerYAnchor),
        ])
        

        self.interaction = UIScrollEdgeElementContainerInteraction()
        self.interaction.scrollView = self.tableView
        self.interaction.edge = .top
        self.headerView.addInteraction(self.interaction)
    }
    
    func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return 100
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        let cell = self.tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
        cell.textLabel?.text = "row \(indexPath.row + 1)"
        return cell
    }
    
}
Answered by DTS Engineer in 852045022

Did you try with a physical device? self.interaction.edge = .top doesn't work for me either on an iOS 26 Beta 4 simulator. (.bottom does work.)

The following code works for me on my iPhone 16 Plus + iOS 26 (Beta 4):

/**
Note: Use a physical iPhone to test this code. The top edge effect doesn't work on an iOS simulator at the time of writing (Beta 4).
 */
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        var text: String = ""
        for _ in 0..<1000 {
            text += "Hello, World, Hello, World, Hello, World, Hello, World, Hello, World, Hello, World!\n"
        }
        
        let scrollView = UITextView()
        scrollView.text = text
        scrollView.frame = view.bounds
        scrollView.contentSize = CGSize(width: view.bounds.width, height: view.bounds.height * 2)
        self.view.addSubview(scrollView)
        
        // TEST1: Top edge.
        var containerView =  UIView()
        var bounds = view.bounds
        containerView.frame = CGRect(
            x: 0, y: 20, width: bounds.width, height: bounds.height / 3
        ).insetBy(dx: 50, dy: 50)

        var interaction = UIScrollEdgeElementContainerInteraction()
        interaction.scrollView = scrollView
        interaction.edge = .top
        containerView.addInteraction(interaction)

        let label = UILabel(frame: containerView.bounds.insetBy(dx: 50, dy: 50))
        label.backgroundColor = .blue
        containerView.addSubview(label)

        view.addSubview(containerView)
        
        // TEST2: Bottom edge.
        containerView =  UIView()
        bounds = view.bounds
        containerView.frame = CGRect(
            x: 0, y: bounds.height - bounds.height / 3, width: bounds.width, height: bounds.height / 3
        ).insetBy(dx: 50, dy: 50)

        interaction = UIScrollEdgeElementContainerInteraction()
        interaction.scrollView = scrollView
        interaction.edge = .bottom
        containerView.addInteraction(interaction)

        let button = UIButton(type: .system)
        button.frame = containerView.bounds.insetBy(dx: 50, dy: 50)
        button.setTitle("Tap Me", for: .normal)
        button.configuration = .glass()
        containerView.addSubview(button)

        view.addSubview(containerView)
    }
}

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

You might want to use UINavigationItem.title or UINavigationItem.titleView instead and speficy the display mode using largeTitleDisplayMode.

For example

title = "Home"
navigationController?.navigationBar.prefersLargeTitles = true
navigationItem.largeTitleDisplayMode = .inline

UIScrollEdgeElementContainerInteraction only apply a visual edge effect treatment for overlapping content in your scroll view . Build a UIKit app with the new design is a good starting up to learn when it's recommended to apply an edge effect and how to.

Thank you. I did watch that video a few times, but I still do not understand.

I have a custom view controller that serves as my navigation controller. I want it to mimic the behavior of the view's title moving up into the title area. Perhaps UIScrollEdgeElementContainerInteraction will not help me with that anyway, I do not know.

The video showed that interaction seeming to blur out content of the view behind it, but I have had no success in implementing that.

In my code above, adding the interaction to a view that overlays the scroll view (the table view) does nothing. I am hoping someone can tell me why it does nothing, or what I am missing. Alternatively, I would love to see code I can run that uses UIScrollEdgeElementContainerInteraction successfully.

Thank you.

John

Accepted Answer

Did you try with a physical device? self.interaction.edge = .top doesn't work for me either on an iOS 26 Beta 4 simulator. (.bottom does work.)

The following code works for me on my iPhone 16 Plus + iOS 26 (Beta 4):

/**
Note: Use a physical iPhone to test this code. The top edge effect doesn't work on an iOS simulator at the time of writing (Beta 4).
 */
class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()

        var text: String = ""
        for _ in 0..<1000 {
            text += "Hello, World, Hello, World, Hello, World, Hello, World, Hello, World, Hello, World!\n"
        }
        
        let scrollView = UITextView()
        scrollView.text = text
        scrollView.frame = view.bounds
        scrollView.contentSize = CGSize(width: view.bounds.width, height: view.bounds.height * 2)
        self.view.addSubview(scrollView)
        
        // TEST1: Top edge.
        var containerView =  UIView()
        var bounds = view.bounds
        containerView.frame = CGRect(
            x: 0, y: 20, width: bounds.width, height: bounds.height / 3
        ).insetBy(dx: 50, dy: 50)

        var interaction = UIScrollEdgeElementContainerInteraction()
        interaction.scrollView = scrollView
        interaction.edge = .top
        containerView.addInteraction(interaction)

        let label = UILabel(frame: containerView.bounds.insetBy(dx: 50, dy: 50))
        label.backgroundColor = .blue
        containerView.addSubview(label)

        view.addSubview(containerView)
        
        // TEST2: Bottom edge.
        containerView =  UIView()
        bounds = view.bounds
        containerView.frame = CGRect(
            x: 0, y: bounds.height - bounds.height / 3, width: bounds.width, height: bounds.height / 3
        ).insetBy(dx: 50, dy: 50)

        interaction = UIScrollEdgeElementContainerInteraction()
        interaction.scrollView = scrollView
        interaction.edge = .bottom
        containerView.addInteraction(interaction)

        let button = UIButton(type: .system)
        button.frame = containerView.bounds.insetBy(dx: 50, dy: 50)
        button.setTitle("Tap Me", for: .normal)
        button.configuration = .glass()
        containerView.addSubview(button)

        view.addSubview(containerView)
    }
}

Best,
——
Ziqiao Chen
 Worldwide Developer Relations.

Thank you so much, this example helps a lot!

Seems like there are still a couple of issues in beta 5.

Both in UIKit (FB19519713) and SwiftUI (FB19519683) the edge effect is too short, especially noticeable with the hard style. In the second screenshot you can see that the bottom view (tinted red) is much higher than the effect.

Also, in UIKit it does not automatically adjust content and scroll indicator insets, which SwiftUI does (FB19519737). Quite annoying.

Filed as FB19519914

We see similar effects. This is a blocker for our iOs 26 Adoption (FB19755107)

Trying to use UIScrollEdgeElementContainerInteraction
 
 
Q