I am creating an app with a YouTube-like PipView (Image-in-Image) construction for stay broadcasting. The app crashes through the transition from the stay broadcast tab to PipView, following these steps:
-
LiveBroadcastVC.swift – Line 300 LiveBroadcastVC.handleDragContainerViewGesture(_:) + 300
-
LiveBroadcastVC.swift – Line 268 LiveBroadcastVC.tabBarFirstView() + 268
-
UIKitCore-[UIViewController dismissViewControllerAnimated:completion:]
-
thunk for @escaping @callee_guaranteed @Sendable () -> ()
-
TabbarVC.swift – Line 124 @objc TabbarVC.selectedIndex.setter + 124
-
TabbarVC.selectedViewController.didset
-
PlayerManager.swift – Line 289 PlayerManager.showPipView() + 289
-
PipView.swift – Line 139 PlayerManager.updatePipViewUI() + 139
-
PipView.swift – Line 210 PipView.addAvPlayer(_:adContainer:) + 210
And I am getting this error:
Deadly Exception: UIViewControllerHierarchyInconsistency youngster view controller:<IMAAdViewController: 0x1226be800> ought to have mum or dad view controller:(null) however precise mum or dad is:<CNNTurk.NativeVideoPlayerViewController: 0x1097cd800>
The crash happens particularly when:
- Person is watching a stay broadcast with an advert enjoying
- Person drags down the stay broadcast view to attenuate it to PipView
- The app makes an attempt to switch the IMAAdViewController to the brand new container
Here is my code blocks;
func showPipView() {
guard let window = UIApplication.shared.keyUIWindow else { return }
if pipView == nil {
pipView = PipView(onTap: didTapPipView,
onPause: didTapPauseButton,
onClose: resetPlayerObjects)
window.addSubview(pipView!)
pipView?.translatesAutoresizingMaskIntoConstraints = false
pipViewBottomConstraint = pipView?
.bottomAnchor
.constraint(equalTo: window.bottomAnchor,
fixed: pipViewBottomConstant)
pipViewBottomConstraint?.isActive = true
pipView?.updateBottomViewIsHidden(isAppearTabBar)
pipView?.widthAnchor.constraint(equalTo: window.widthAnchor).isActive = true
window.bringSubviewToFront(pipView!)
updatePipViewUI()
}
}
personal func updatePipViewUI() {
guard let videoLayerType,
let currentPlayerLayer else { return }
pipView?.addPlayerLayer(currentPlayerLayer, adContainer: currentPlayer?.adContainer)
pipView?.updateTitle(videoLayerType.title, subtitle: videoLayerType.subtitle)
pipView?.updatePauseButtonImage(isPlaying: isPlaying)
}
func addPlayerLayer(_ playerLayer: Any, adContainer: AnyObject?) {
swap playerLayer {
case let avPlayer as AVPlayer:
addAvPlayer(avPlayer, adContainer: adContainer)
default: fatalError()
}
}
func addAvPlayer(_ avPlayer: AVPlayer, adContainer: AnyObject?) {
let playerLayer = AVPlayerLayer(participant: avPlayer)
playerLayer.videoGravity = .resizeAspectFill
playerView.layer.addSublayer(playerLayer)
playerView.layoutIfNeeded()
playerLayer.body = CGRect(origin: .zero, measurement: playerView.bounds.measurement)
if PlayerManager.shared.isPlayingAd {
if let adView = (adContainer as? UIViewController)?.view.subviews.final {
adView.translatesAutoresizingMaskIntoConstraints = false
playerView.addSubview(adView)
PlayerManager.shared.currentPlayer?.adContainer = adView
adView.leadingAnchor.constraint(equalTo: playerView.leadingAnchor).isActive = true
adView.topAnchor.constraint(equalTo: playerView.topAnchor).isActive = true
adView.trailingAnchor.constraint(equalTo: playerView.trailingAnchor).isActive = true
adView.bottomAnchor.constraint(equalTo: playerView.bottomAnchor).isActive = true
} else if let adView = adContainer as? UIView {
playerView.addSubview(adView)
adView.backgroundColor = .clear
adView.leadingAnchor.constraint(equalTo: playerView.leadingAnchor).isActive = true
adView.topAnchor.constraint(equalTo: playerView.topAnchor).isActive = true
adView.trailingAnchor.constraint(equalTo: playerView.trailingAnchor).isActive = true
adView.bottomAnchor.constraint(equalTo: playerView.bottomAnchor).isActive = true
}
}
}
How can I correctly handle the view controller hierarchy when transitioning the Google IMA adverts view controller between containers? Any assist can be appreciated.