2016-03-23 7 views
2

Ich versuche ein UITableView einzurichten, das Videos abspielen kann. Viele der vorherigen SO-Fragen dazu verwendet MPMoviePlayer (Playing Video into UITableView, Playing video in UItableView in SWIFT, Playing Video From UITableView), die jetzt in iOS 9 veraltet ist. Einer der wenigen, die AVFoundation (was ich verwende), ist dieser: Play video on UITableViewCell when it is completely visible und ist, wo ich Ich bekomme den größten Teil meines Codes. Hier ist mein Code, innen cellForRowAtIndexPath:Video in der uitableview Zelle abspielen

VideoCell *videoCell = (VideoCell *)[self.tableView dequeueReusableCellWithIdentifier:@"VideoCell"]; 

     dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul); 
     NSURL *url = [[NSURL alloc] initWithString:urlString]; 

     dispatch_async(queue, ^{ 
     videoCell.item = [AVPlayerItem playerItemWithURL:url]; 

      dispatch_sync(dispatch_get_main_queue(), ^{ 
       videoCell.player = [[AVPlayer alloc] initWithPlayerItem:videoCell.item]; 
       videoCell.player.actionAtItemEnd = AVPlayerActionAtItemEndNone; 

       AVPlayerLayer *playerLayer = [AVPlayerLayer playerLayerWithPlayer:videoCell.player]; 
       playerLayer.frame = CGRectMake(0, 0, videoCell.contentView.frame.size.width, videoCell.contentView.frame.size.height); 
       [videoCell.contentView.layer addSublayer:playerLayer]; 
       playerLayer.videoGravity = AVLayerVideoGravityResize; 
       [videoCell.player play]; 

      }); 
     }); 


     return videoCell; 

Von dem, was ich verstehe, muss ich asynchron die Videos herunterladen, bevor ich sie spielen. Ich habe Bilder vorher asynchron heruntergeladen und der "Download" Teil beinhaltet immer die Konvertierung von NSURL -> NSData -> UIImage. Und dann, wenn du den UIImage hast, kannst du ihn in der Zelle anzeigen, also bringst du die Hauptwarteschlange hoch und dispatch_async und führst die cell.imageView.image = yourImage aus; in der Hauptwarteschlange.

Hier habe ich eine NSURL, aber ich verstehe nicht ganz, welche Schritte hier in der Hauptwarteschlange sein sollte und welche aus dem Hauptthread sein sollte. Ich habe versucht, was in der oben genannten SO Frage vorgeschlagen wurde, aber bis jetzt funktioniert es nicht. Die Tabellenzelle wird nur geladen, und das Video wird nicht abgespielt. Ich sehe nur den ersten Frame. Manchmal wird die erste 1 Sekunde abgespielt, aber dann puffert es danach und wird es nicht tun. Ich arbeite mit einer Tabellenansicht, die momentan nur über 1 Objekt verfügt. Es gibt also nur ein Video, das wiedergegeben werden kann und es wird immer noch nicht abgespielt.

Was mache ich falsch, und könnte mir jemand helfen, genau zu erklären, welche Teile am Hauptfaden sein müssen und welche? Ein Antwortender in dem Thread, den ich oben erwähnt habe, sagte, es gebe "viele Tutorials dazu", aber nachdem ich Google gescannt habe, habe ich keine gesehen. Die Begriffe "asynchron" und "iOS" zusammen erhalten fast immer Suchergebnisse zum Herunterladen von Bildern, nicht Video. Aber wenn irgendwelche Tutorials existieren, wäre es schön, einen zu sehen.

dank

Antwort

2

Im Folgenden ist die Methode, die ich verwendet habe, Video in den Zellen Tableview zu spielen. Ich bin mir nicht sicher, es ist die beste Methode dafür, trotzdem kann es Ihnen helfen :)

Zuerst habe ich eine benutzerdefinierte Zelle erstellt. In der Setter-Methode habe ich eine Methode zum Festlegen von AVPlayer aufgerufen.

- (void)setUpAVPlayer 
{ 
    @try { 
     self.videoPlayer = [[AVPlayer alloc]initWithPlayerItem:self.videoPlayerItem]; 
    } 
    @catch (NSException *exception) { 
     NSLog(@"Exception : %@",exception.description); 
     [self.videoPlayer replaceCurrentItemWithPlayerItem:self.videoPlayerItem]; 
    } 


    // Setting player properties. 
    playerLayer = [AVPlayerLayer playerLayerWithPlayer:self.videoPlayer]; 
    playerLayer.videoGravity = AVLayerVideoGravityResize; 
    [self.previewImageView.layer addSublayer:playerLayer]; 
    self.previewImageView.clipsToBounds = YES; 
    playerLayer.hidden = YES; 

    [playerLayer addObserver:self forKeyPath:@"readyForDisplay" options:0 context:nil]; 
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(pauseAllRunningPlayers) name:@"pause" object:nil]; 


    // AVPlayer KVO 

    [self.videoPlayer addObserver:self forKeyPath:@"rate"       options:NSKeyValueObservingOptionNew context:kRateDidChangeKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.status"    options:NSKeyValueObservingOptionNew context:kStatusDidChangeKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.duration"   options:NSKeyValueObservingOptionNew context:kDurationDidChangeKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.loadedTimeRanges" options:NSKeyValueObservingOptionNew context:kTimeRangesKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.playbackBufferFull" options:NSKeyValueObservingOptionNew context:kBufferFullKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.playbackBufferEmpty" options:NSKeyValueObservingOptionNew context:kBufferEmptyKVO]; 
    [self.videoPlayer addObserver:self forKeyPath:@"currentItem.error"    options:NSKeyValueObservingOptionNew context:kDidFailKVO]; 

    [videoLoadingIndicator stopAnimating]; 

} 

Ich habe festgestellt, dass Sie in Ihrem Code theb Play-Funktion geschrieben haben, wie diese [videoCell.player play];

Sie nur die Play-Funktion aufrufen soll, wenn der Player-Status ‚AVPlayerStatusReadyToPlay‘ wird. Deshalb habe ich KVOs benutzt.

Hope this may hilft :) Sie

+0

so rufen Sie diese in cellForRowAtIndexPath? – joey

+0

Nein Ich habe eine benutzerdefinierte Zellklasse erstellt, und ich habe die oben genannte Funktion innerhalb der Datensettermethode aufgerufen. –

+0

Können Sie ein Beispielprojekt erstellen? Danke –

1

Code unten in Ihrem cellForRowAtIndexPath fügen Sie einfach.

player = [AVPlayer playerWithURL:media.lowResolutionVideoURL]; // 
layer = [AVPlayerLayer layer]; 
[layer setPlayer:player]; 
layer.frame = @"Your Frame"; 
[layer setBackgroundColor:[UIColor clearColor].CGColor]; 
[layer setVideoGravity:AVLayerVideoGravityResizeAspect]; 
[cell.contentView.layer addSublayer:layer]; 
[player play]; 
0
  • Hier habe ich eine Demo vorbereitet, in dem Sobald Ihre Zelle sichtbar sein wird, wie pro Ihre Anforderung Spieler ein Video abspielen startet, können Sie Ihr Storyboard mit dem Referenz von unten Bild entwerfen.

    // 
    // ViewController.swift 
    // RTLDemo 
    // 
    // Created by iOS Test User on 06/01/18. 
    // Copyright © 2018 iOS Test User. All rights reserved. 
    // 
    
    import UIKit 
    import AVKit 
    
    public struct VideoName { 
        static let video1 = "video1" 
        static let video2 = "video2" 
    } 
    
    public struct MediaType { 
        static let video = 1 
        static let image = 2 
    } 
    
    class ViewController: UIViewController { 
    
        @IBOutlet weak var collView: UICollectionView! 
        var timer: Timer? 
        var isVideoPlaying: Bool = false 
    
        // ------------------------------------------------------------------------------------------ 
        // MARK: - 
        // MARK: - Memory management method 
    
        // ------------------------------------------------------------------------------------------ 
    
        override func didReceiveMemoryWarning() { 
         super.didReceiveMemoryWarning() 
         // Dispose of any resources that can be recreated. 
        } 
    
        // ------------------------------------------------------------------------------------------ 
        // MARK: 
        // MARK: - Custom Methods 
    
        // ------------------------------------------------------------------------------------------ 
    
        func initialSetup() { 
         if self.timer == nil { 
          self.timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(checkForTheVisibleVideo), userInfo: nil, repeats: true) 
         } 
         timer?.fire() 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        @objc func checkForTheVisibleVideo() { 
         if !isVideoPlaying { 
          let visibleCell = self.collView.indexPathsForVisibleItems 
          if visibleCell.count > 0 { 
           for indexPath in visibleCell { 
            if self.isVideoPlaying { 
             break 
            } 
            if let cell = self.collView.cellForItem(at: indexPath) as? CustomCell,cell.mediaType == MediaType.video { 
             if cell.player == nil{ 
              cell.player = AVPlayer(url: URL.init(fileURLWithPath: Bundle.main.path(forResource: VideoName.video2, ofType: "mp4")!)) 
              cell.playerLayer = AVPlayerLayer.init(player: cell.player) 
              cell.playerLayer?.frame = cell.imgView.frame 
              cell.imgView.layer.addSublayer(cell.playerLayer!) 
              NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: cell.player?.currentItem) 
              cell.player?.addPeriodicTimeObserver(forInterval: CMTime.init(seconds: 1, preferredTimescale: 1), queue: .main, using: { (time) in 
               if cell.player?.currentItem?.status == .readyToPlay { 
    
                let timeDuration : Float64 = CMTimeGetSeconds((cell.player?.currentItem?.asset.duration)!) 
                cell.lblDuration.text = self.getDurationFromTime(time: timeDuration) 
    
                let currentTime : Float64 = CMTimeGetSeconds((cell.player?.currentTime())!) 
                cell.lblStart.text = self.getDurationFromTime(time: currentTime) 
                cell.slider.maximumValue = Float(timeDuration.rounded()) 
                cell.slider.value = Float(currentTime.rounded()) 
               } 
              }) 
             } 
             cell.player?.play() 
             cell.btnPlay.setImage(#imageLiteral(resourceName: "pause_video"), for: .normal) 
             self.isVideoPlaying = true 
            } 
           } 
          } 
         } 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        @objc func videoDidFinishPlaying() { 
         self.isVideoPlaying = false 
         let visibleItems: Array = self.collView.indexPathsForVisibleItems 
    
         if visibleItems.count > 0 { 
    
          for currentCell in visibleItems { 
    
           guard let cell = self.collView.cellForItem(at: currentCell) as? CustomCell else { 
            return 
           } 
           if cell.player != nil { 
            cell.player?.seek(to: kCMTimeZero) 
            cell.player?.play() 
           } 
          } 
    
         } 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        @objc func onSliderValChanged(slider: UISlider, event: UIEvent) { 
         if let touchEvent = event.allTouches?.first { 
          guard let cell = self.collView.cellForItem(at: IndexPath.init(item: slider.tag, section: 0)) as? CustomCell else { 
           return 
          } 
          switch touchEvent.phase { 
          case .began: 
           cell.player?.pause() 
          case .moved: 
           cell.player?.seek(to: CMTimeMake(Int64(slider.value), 1)) 
          case .ended: 
           cell.player?.seek(to: CMTimeMake(Int64(slider.value), 1)) 
           cell.player?.play() 
          default: 
           break 
          } 
         } 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        func getDurationFromTime(time: Float64)-> String { 
    
         let date : Date = Date(timeIntervalSince1970: time) 
         let dateFormatter = DateFormatter() 
         dateFormatter.timeZone = TimeZone.init(identifier: "UTC") 
         dateFormatter.dateFormat = time < 3600 ? "mm:ss" : "HH:mm:ss" 
         return dateFormatter.string(from: date) 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        @IBAction func btnPlayTapped(_ sender: UIButton) { 
    
         let indexPath = IndexPath.init(item: sender.tag, section: 0) 
         guard let cell = self.collView.cellForItem(at: indexPath) as? CustomCell else { 
          return 
         } 
         if isVideoPlaying { 
          self.isVideoPlaying = false 
          cell.btnPlay.setImage(#imageLiteral(resourceName: "play_video"), for: .normal) 
          cell.player?.pause() 
         }else{ 
          if cell.player == nil { 
           cell.player = AVPlayer(url: URL.init(fileURLWithPath: Bundle.main.path(forResource: VideoName.video2, ofType: "mp4")!)) 
           cell.playerLayer = AVPlayerLayer(player: cell.player!) 
           cell.playerLayer?.frame = CGRect.init(x: 0, y: 0, width: UIScreen.main.bounds.size.width - 50, height: (UIScreen.main.bounds.size.height - 64) * 0.3) 
           cell.imgView.layer.addSublayer(cell.playerLayer!) 
           NotificationCenter.default.addObserver(self, selector: #selector(videoDidFinishPlaying), name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: cell.player?.currentItem) 
          } 
          cell.btnPlay.setImage(#imageLiteral(resourceName: "pause_video"), for: .normal) 
          cell.player?.play() 
          self.isVideoPlaying = true 
         } 
    
        } 
    
        // ------------------------------------------------------------------------------------------ 
        // MARK: - 
        // MARK: - View life cycle methods 
        // ------------------------------------------------------------------------------------------ 
    
        override func viewDidLoad() { 
         super.viewDidLoad() 
         self.initialSetup() 
        } 
    } 
    
    extension ViewController: UICollectionViewDataSource, UICollectionViewDelegate,UICollectionViewDelegateFlowLayout { 
    
        func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 
         return 10 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { 
         let cell = self.collView.dequeueReusableCell(withReuseIdentifier: "CustomCell", for: indexPath) as! CustomCell 
         if indexPath.row % 2 == 0 { 
          cell.mediaType = MediaType.image 
          cell.btnPlay.isHidden = true 
          cell.lblDuration.isHidden = true 
          cell.lblStart.isHidden = true 
          cell.slider.isHidden = true 
          cell.imgView.isHidden = false 
         }else{ 
          cell.mediaType = MediaType.video 
          cell.btnPlay.isHidden = false 
          cell.lblDuration.isHidden = false 
          cell.lblStart.isHidden = false 
          cell.slider.isHidden = false 
          cell.imgView.isHidden = false 
         } 
         cell.btnPlay.tag = indexPath.row 
         cell.slider.tag = indexPath.row 
         cell.btnPlay.addTarget(self, action: #selector(btnPlayTapped(_:)), for: .touchUpInside) 
         cell.slider.addTarget(self, action: #selector(self.onSliderValChanged(slider:event:)), for: .valueChanged) 
    
         return cell 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { 
         return CGSize.init(width: UIScreen.main.bounds.size.width, height: UIScreen.main.bounds.size.height/3) 
        } 
    
        // ------------------------------------------------------------------------------------------ 
    
        func collectionView(_ collectionView: UICollectionView, didEndDisplaying cell: UICollectionViewCell, forItemAt indexPath: IndexPath) { 
    
         guard let cellToHide = cell as? CustomCell else { 
          return 
         } 
    
         if cellToHide.player != nil { 
          cellToHide.player?.pause() 
          cellToHide.playerLayer?.removeFromSuperlayer() 
          cellToHide.player = nil 
          cellToHide.btnPlay.setImage(#imageLiteral(resourceName: "play_video"), for: .normal) 
          self.isVideoPlaying = false 
         } 
    
        } 
    } 
    
    // Custom Cell Class 
    
    class CustomCell: UICollectionViewCell { 
    
        @IBOutlet weak var btnPlay: UIButton! 
        @IBOutlet weak var lblDuration: UILabel! 
        @IBOutlet weak var imgView: UIImageView! 
        @IBOutlet weak var viewBack: UIView! 
        @IBOutlet weak var lblStart: UILabel! 
        @IBOutlet weak var slider: UISlider! 
    
        var player: AVPlayer? 
        var playerLayer: AVPlayerLayer? 
        var mediaType: Int! 
    } 
    
  • Storyboard-Referenzbild, in dem ich mit Zelle Sammlung Ansicht einfachen benutzerdefinierten Video-Player erstellt:

enter image description here