Gesture of child View not working in SwiftUI

Viewed 207

Using Swift5.3.2, iOS14.4.1, Xcode12.4,

I am trying to make two simultaneous gestures work in SwiftUI.

The child-View at question is a VideoPlayer.

The parent-View is a ZStack.

I am using the .simultaneousGesture modifier, hoping that this will allow all child-View gestures to still go through.

But they don't !

The following code works but does not allow the VideoPlayer's control gestures to be recognised.

In fact, the restartScannerTapGesture TapGesture in my example does always kick in - and the VideoPlayer control gestures (like pause, play, stop etc.) are unfortunately ignored.

Any idea on how to get child-View gestures work when added a gesture to a parent-View in Swift UI ??

struct ParentView: View {

    @EnvironmentObject var myURLList

    var body: some View {
        
        let restartScannerTapGesture = TapGesture(count: 1)
            .onEnded {
                actionClickID = UUID()
            }

        ZStack {
            if let url = URL(fileURLWithPath: myURLList[0].path){
                if url.containsImage {
                    Image(uiImage: UIImage(contentsOfFile: url.path)!)
                        .resizable()
                        .scaledToFit()
                        .onAppear() {
                            isVideo = false
                        }
                } else if url.containsVideo {
                    CustomPlayerView(url: url, isVideo: $isVideo)
                        .onAppear() {
                            isVideo = true
                        }
                } else {
                    Text(LocalizedStringKey("MediaNotRecognizedKey"))
                        .multilineTextAlignment(.center)
                        .padding()
                        .onAppear() {
                            isVideo = false
                        }
                }
            } else {
                Text(LocalizedStringKey("MediaNotRecognizedKey"))
                    .multilineTextAlignment(.center)
                    .padding()
                    .onAppear() {
                        isVideo = false
                    }
            }
        }
    }
    .simultaneousGesture(restartScannerTapGesture)
}

And here is the CustomPlayerView that shows the Video controls in the first place. It clearly is the child-View at question. Its gestures should also work, but they don't. Why ??

import SwiftUI
import AVKit

class PlayerViewModel: ObservableObject {
    
    @Published var avPlayer: AVPlayer?
    
    func loadFromUrl(url: URL) {
        avPlayer = AVPlayer(url: url)
    }
    
    func playVideo() {
        avPlayer?.play()
    }
    
    func stopVideo() {
        avPlayer?.pause()
        avPlayer?.replaceCurrentItem(with: nil)
    }
}

struct CustomPlayerView: View {
    
    var url : URL
    @Binding var isVideo: Bool
    @StateObject private var playerViewModel = PlayerViewModel()
    
    var body: some View {
        
           ZStack {
        if let url = URL(fileURLWithPath: list.paths[index]){
            if url.containsImage {
                Image(uiImage: UIImage(contentsOfFile: url.path)!)
                    .resizable()
                    .scaledToFit()
                    .onAppear() {
                        isVideo = false
                    }
            } else if url.containsVideo {
                CustomPlayerView(url: url, isVideo: $isVideo)
                    .onAppear() {
                        isVideo = true
                    }
            } else {
                Text(LocalizedStringKey("MediaNotRecognizedKey"))
                    .multilineTextAlignment(.center)
                    .padding()
                    .onAppear() {
                        isVideo = false
                    }
            }
        } else {
            Text(LocalizedStringKey("MediaNotRecognizedKey"))
                .multilineTextAlignment(.center)
                .padding()
                .onAppear() {
                    isVideo = false
                }
        }
    }
}

And the needed extension:

extension URL {
    func mimeType() -> String {
         let pathExtension = self.pathExtension
         if let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as NSString, nil)?.takeRetainedValue() {
             if let mimetype = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassMIMEType)?.takeRetainedValue() {
                return mimetype as String
             }
         }
         return "application/octet-stream"
    }

    var containsImage: Bool {
        let mimeType = self.mimeType()
        guard let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeRetainedValue() else {
             return false
        }
        return UTTypeConformsTo(uti, kUTTypeImage)
    }

    var containsAudio: Bool {
        let mimeType = self.mimeType()
        guard let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeRetainedValue() else {
              return false
        }
        return UTTypeConformsTo(uti, kUTTypeAudio)
    }
    
    var containsVideo: Bool {
        let mimeType = self.mimeType()
        guard  let uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mimeType as CFString, nil)?.takeRetainedValue() else {
               return false
        }
        return UTTypeConformsTo(uti, kUTTypeMovie)
    }
 }
0 Answers
Related