TextureGroup / Texture

Smooth asynchronous user interfaces for iOS apps.
https://texturegroup.org/
Other
8.02k stars 1.3k forks source link

VideoNode Tap to Fullscreen with Smooth Instagram-like Transition #2117

Open abbasnaqvi200 opened 2 months ago

abbasnaqvi200 commented 2 months ago

Hey, could you please help me with code for displaying videos in a table view using ASVideoNode? When the video is tapped, it should transition smoothly to fullscreen, similar to Instagram’s video experience. I’d also like the video to continue playing from where it left off during the transition. Smooth animation is essential for a seamless user experience.

My Video Cell Code is:-

class WallVideoNode: ASCellNode {

let videoNode = ASVideoNode()
let muteButton = ASButtonNode()

var size = CGSize(width:UIScreen.main.bounds.width, height: UIScreen.main.bounds.width)

override init() {
    super.init()
    setupVideoNode()
}

init(size : CGSize) {
    super.init()
    self.size = size
    setupVideoNode()
}

var videoURL : URL?
var mediaFile : MediaFilePath?
var isPlayingOnFullScreen = false

private func setupVideoNode() {
    videoNode.delegate = self
    videoNode.shouldAutorepeat = true
    //videoNode.shouldAutoplay = true
    videoNode.backgroundColor = .black
    videoNode.muted = WallView.currentMuteStatus
    addSubnode(videoNode)
    addSubnode(muteButton)
}

override func layoutSpecThatFits(_ constrainedSize: ASSizeRange) -> ASLayoutSpec {

    return LayoutSpec {
        videoNode.preferredSize(size).overlay {

            InsetLayout(insets: UIEdgeInsets(top: .infinity, left: .infinity, bottom: 10, right: 10)) {
                muteButton.preferredSize(CGSize(width: 30, height: 30))
            }
        }
    }
}

func configureVideo(with mediaFile: MediaFilePath) {
    self.mediaFile = mediaFile

    if let url = URL.init(string: mediaFile.wall_Media_Path ?? "") {
        self.videoURL = url

        DispatchQueue.global(qos: .background).async {
            let asset = AVAsset(url: url)
            asset.loadValuesAsynchronously(forKeys: ["playable"], completionHandler: {
                DispatchQueue.main.async {
                    asset.cancelLoading()
                    self.videoNode.asset = asset
                }
            })
        }
    }

    let placeholder =  mediaFile.gcs_Video_Placeholder ?? ""

    self.setWallImage(placeholder, media: mediaFile)

    videoNode.gravity = AVLayerVideoGravity.resizeAspectFill.rawValue // Set Aspect Fill
    muteButton.tintColor = .white
    muteButton.cornerRadius = 15
    videoNode.muted = WallView.currentMuteStatus
    muteButton.isSelected = WallView.currentMuteStatus
    muteButton.backgroundColor = UIColor.black.withAlphaComponent(0.3)
    let size = CGSize(width: 12, height: 12)
    muteButton.setImage(UIImage(named: "ct_unmute_white")?.resize(targetSize:size ), for: .normal)
    muteButton.setImage(UIImage(named: "ct_mute_white")?.resize(targetSize: size), for: .selected)
    muteButton.addTarget(self, action: #selector(muteVideo), forControlEvents: .touchUpInside)
}

func setMuteUnmute() {
    muteButton.isSelected = WallView.currentMuteStatus
    videoNode.muted = WallView.currentMuteStatus
}

@objc func muteVideo() {
    muteButton.isSelected = !muteButton.isSelected
    videoNode.muted = muteButton.isSelected
    WallView.currentMuteStatus = muteButton.isSelected
}

func playVideo() {
    videoNode.play()
}

func pauseVideo() {
    videoNode.pause()
}

}

extension WallVideoNode : ASVideoNodeDelegate {

// ASVideoNodeDelegate method
func didTap(_ videoNode: ASVideoNode) {

    if isPlayingOnFullScreen {

        self.closestViewController?.presentedViewController?.dismiss(animated: true)
        self.insertSubnode(videoNode, at: 0)
        self.isPlayingOnFullScreen = false

    } else {
        isPlayingOnFullScreen = true
        print("Video node tapped!")
        ///videoNode.gravity = AVLayerVideoGravity.resizeAspect.rawValue // Set Aspect Fill
        guard let asset = videoNode.asset else { return }
        //pauseVideo() // Pause the current video and save playback time
        self.transitionToFullScreen()
    }
}

}

extension WallVideoNode {

func transitionToFullScreen() {
    // 1. Get the snapshot of the video node
    guard let vc = self.closestViewController else {return}

    let heroId = "cell\(1)"

    // Setting Hero ID for transition
    let fullScreenSize = CGSize(width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)
    videoNode.frame = CGRect(origin: .zero, size: fullScreenSize)
    videoNode.layoutIfNeeded()

    videoNode.view.hero.id = heroId

    let detailVC = FullScreenVideoViewController(videoNode: videoNode)
    detailVC.modalPresentationStyle = .overFullScreen
    detailVC.hero.isEnabled = true
    //detailVC.videoNode.view.hero.id = heroId

    detailVC.dismissUpdate = {
        self.insertSubnode(self.videoNode, at: 0)
        self.isPlayingOnFullScreen = false
    }

    vc.present(detailVC, animated: false, completion: nil)

}

}