1

I am trying to add a banner window which should show on top of the main window at all times. nothing from the main window should ever block the content on that banner window and the banner window should also not block the content of the main window. The following code worked fine on an iPhone but on an iPad with split view enabled I am facing some issues.

This is how it looks normally enter image description here

I manually set the frames of both the windows and this is causing an issue when the width of the scene changes as we modify the width available for the scene in the split view.

How can I watch for changes in the available width for the app's scene in split view?

WHAT I TRIED:

I tried using viewWillLayoutSubviews, traitCollectionDidChange and also UIContentContainer protocol methods as shown in the code below, none of those methods are called when I change the width available for the app on split view. They only get called when the orientation changes.

Complete code for the project is available here https://github.com/anirudhbandi96/WindowTest

import UIKit


class ViewController: UIViewController {
    
    let label: UILabel = {
        let label = UILabel()
        label.numberOfLines = 0
        label.font = UIFont.preferredFont(forTextStyle: .largeTitle)
        label.textAlignment = .center
        label.textColor = .white
        return label
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.backgroundColor = .systemGreen
        
        
        label.text = "This is the key window"
        view.addSubview(label)
        label.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            label.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            label.topAnchor.constraint(equalTo: view.topAnchor),
            label.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            label.trailingAnchor.constraint(equalTo: view.trailingAnchor)
        ])
    }
    
    override func viewWillLayoutSubviews() {
        super.viewWillLayoutSubviews()
        print("laying out subviews")
    }


    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        
        // Just added a small delay to see the addition of the banner window clearly
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2.0) {
            
            
            guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else { return }

            // height of the Banner window
            let height: CGFloat = 100
            
            guard let window = self.view.window,
                  let windowScene = window.windowScene,
                  let keyWindow = windowScene.windows.first(where: { $0.isKeyWindow })
            else { return }
            
            // accessing the key window and changing its frame so that the window's content is not
            // blocked by the banner window that is going to be above it
            let size = keyWindow.frame.size
            keyWindow.frame = CGRect(origin: CGPoint(x: 0, y: height), size: CGSize(width: size.width, height: size.height - height))
            
            // Creating a banner window
            let bannerWindow = UIWindow(windowScene: windowScene)
            bannerWindow.frame = CGRect(origin: .zero, size: CGSize(width: size.width, height: height))
            bannerWindow.isHidden = false
            
            // Creating a banner view and adding it to the banner window
            let bannerView = UIView(frame: .zero)
            bannerView.backgroundColor = .systemPink
            bannerView.translatesAutoresizingMaskIntoConstraints = false
            
            bannerWindow.addSubview(bannerView)
            bannerWindow.windowLevel = keyWindow.windowLevel + 1
            NSLayoutConstraint.activate([
                bannerView.leadingAnchor.constraint(equalTo: bannerWindow.leadingAnchor),
                bannerView.trailingAnchor.constraint(equalTo: bannerWindow.trailingAnchor),
                bannerView.heightAnchor.constraint(equalToConstant: height),
                bannerView.bottomAnchor.constraint(equalTo: bannerWindow.bottomAnchor),
            ])
            
            // Keeping a reference to the banner window to prevent it from being deallocated
            appDelegate.bannerWindow = bannerWindow
        }
      
    }
    
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
        super.traitCollectionDidChange(previousTraitCollection)
        print("Trait collection changed")
    }
    
    override func willTransition(to newCollection: UITraitCollection, with coordinator: UIViewControllerTransitionCoordinator) {
        super.willTransition(to: newCollection, with: coordinator)
        print("will transition to new collection")
    }
    
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        super.viewWillTransition(to: size, with: coordinator)
        print("will transtion to size: \(size)")
    }
}

When split screen is enabled it is still using the frame that was set previously for the full screen which makes sense.

enter image description here

enter image description here

Anirudh Bandi
  • 1,171
  • 10
  • 20

1 Answers1

0
 override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator)

this method is the right place for making changes whenever view size changes. Try to put the breakpoint, is called whenever you are expecting it.

Apple documentation: https://developer.apple.com/documentation/uikit/uicontentcontainer/1621466-viewwilltransition

Manish Punia
  • 747
  • 4
  • 9
  • I did try this and that method never got called when I resized the scene on split view. Did you try this on the project link I attached? – Anirudh Bandi Nov 20 '20 at 14:44
  • If I did not modify the key window's frame and just added the new banner window on top of it, I do see the `viewWillTransition` getting called with the right scene size but with the frame of the key window set manually that never happens – Anirudh Bandi Nov 20 '20 at 14:46