简体   繁体   中英

New UIView blocking UITapGesture

I have a video in the background of every page of my scrollView with paging enabled. I want the video to play/pause when the area where one can see the video is tapped (see picture on the left below, between UIView containing label with name and image and bottom tab bar)(there is another subview over the video subview in each page: top info). The code used to achieve that is the following for loop (for loop because every page shall have these properties):

//add subviews (pages)
for (pageIndex, page) in pagesViews.enumerated(){

    //...create pages with views

    //add Subview with videoplayer frame to individual "pages"
    let videoPlayerFrame = CGRect(x: 0, y: 0, width: pageSize.width, height: pageSize.height)
    let videoPlayerView = PlayerView(frame: videoPlayerFrame)
    page.addSubview(videoPlayerView)

    //add Subview with top info frame to individual "pages"
    let postInfoFrame = CGRect(x: 0, y: 0, width: pageSize.width, height: 80)
    let infoView = PostView(frame: postInfoFrame)
    page.addSubview(infoView)

    //WORKS FINE UNTIL HERE


    //NEW SUBVIEW
    //add Subview with bottom info frame to individual "pages"
    let postPropertiesFrame = CGRect(x: 0, y: (pageSize.height - 200) / 2, width: pageSize.width, height: 200)
    let propertiesView = PostPropsView(frame: postPropertiesFrame)
    page.addSubview(propertiesView)

}

That's what it looks like if the new subview hasn't been added: 直到这里工作

PlayerView class serving as basis of first subview, the video plays/pauses when I tap the video area (read comments to get overview first (eg spinner shouldn't be important)):

lass PlayerView: UIView {

    //create spinner view
    let activityIndicatorView: UIActivityIndicatorView = {

        //define properties
        let aiv = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
        aiv.translatesAutoresizingMaskIntoConstraints = false

        //start spinner
        aiv.startAnimating()

        return aiv

    }()

    //create controls container view containing the spinner
    let controlsContainerView: UIView = {

        //set properties of controls container view
        let controlView = UIView()
        controlView.backgroundColor = UIColor(white: 0, alpha: 1)

        return controlView

    }()

    override init(frame: CGRect){

        super.init(frame: frame)

        //function below this override init
        setupPlayerView()

        //add subview with controls (e.g. spinner)
        controlsContainerView.frame = frame
        addSubview(controlsContainerView)

        //add to subview and center spinner in subview
        controlsContainerView.addSubview(activityIndicatorView)
        activityIndicatorView.centerXAnchor.constraint(equalTo: centerXAnchor).isActive = true
        activityIndicatorView.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true

        //enable interaction
        controlsContainerView.isUserInteractionEnabled = true

        //function below this override init
        defInteractions()

        //backgorund color of player
        backgroundColor = .black

    }

    //define interactions (doupletap and singletap) 
    func defInteractions (){

        //singletap
        let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapDetected(_:)))
        singleTap.numberOfTapsRequired = 1

        //controlsContainerView
        controlsContainerView.addGestureRecognizer(singleTap)
    }

    //define type
    var player: AVPlayer?

    //set playing to false
    var isPlaying: Bool = false

    //PLAY?PAUSE VIDEO WHEN video tapped
    func singleTapDetected(_ sender: UITapGestureRecognizer) {

        //play or pause
        if(!isPlaying){

            //play
            player?.play()
            isPlaying = true

        }
        else{

            //pause
            player?.pause()
            isPlaying = false

        }

    }

    //setup video in background
    func setupPlayerView() {

        //insert url
        let urlString = "https://blurtime.com/images/testvideo.mov"

        //check URL if can be converted to NSURL
        if let videoURL = NSURL(string: urlString){

            //player's video
            if self.player == nil {
                player = AVPlayer(url: videoURL as URL)
            }

            //add sub-layer
            let playerLayer = AVPlayerLayer(player: player)
            self.layer.addSublayer(playerLayer)
            playerLayer.frame = self.frame

            //when are frames actually rendered (when is video loaded)
            player?.addObserver(self, forKeyPath: "currentItem.loadedTimeRanges", options: .new, context: nil)

            //loop through video
            NotificationCenter.default.addObserver(forName: .AVPlayerItemDidPlayToEndTime, object: self.player?.currentItem, queue: nil, using: { (_) in
                DispatchQueue.main.async {
                    self.player?.seek(to: kCMTimeZero)
                    self.player?.play()
                }
            })

        }

    }

    //what to do when video is loaded (spinner shall disappear)
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {

        //when frames are actually being rendered
        if keyPath == "currentItem.loadedTimeRanges" {

            activityIndicatorView.stopAnimating()
            controlsContainerView.backgroundColor = .clear

        }

    }

    //reuired as suggested by XCode
    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

}

However, if I add the subview with the PostPropsView class it doesn't completely work anymore. If the yellow areas are tapped it still functions as desired but not in the red area anymore... (picture on the right) Why does it work in the lower yellow area and why doesn't it anymore if the red area is tapped? Can anyone provide a solution (and explanation) to this problem?

The new view is on top of the view that has the tap gesture recognizer, and is intercepting those touches, and doesn't have a handler for them.

Perhaps create a new UIView, which contains both the PostPropsView and your video player, and put the tap gesture recognizer on that instead of the video? I don't believe you can add the UIView directly to the video player.

As Jake T. said, the tap gesture recognizer is absorbing the tap, so what you have to do is to add this on your TapGesture declaration

let singleTap = UITapGestureRecognizer(target: self, action: #selector(singleTapDetected(_:)))
singleTap.numberOfTapsRequired = 1
singleTap.cancelsTouchesInView = false

With that, you'll get what you want

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM