2016-12-22 2 views
-2

저는 GitHub 프로젝트를 개선하기 위해 노력하고 있습니다 (https://github.com/giacmarangoni/Swift-Radio-Pro/tree/xcode8). 일부 수정 및 변경 작업이 모두 잘된 것 같지만 갑자기 정말 이상한 행동을 발견했습니다. "NowPlayingViewController"를 처음 열었을 때 방송국에서 스트리밍을 시작하면 모든 것이 작동하고 AVPlayer 대리인이 사용자 인터페이스를 예상대로 업데이트합니다 (songLabel, titleLabel 및 albumArtwork). 그런 다음 라디오 스트리밍을 중단하지 않고 "StationsViewController"로 돌아가서 "지금 재생 중"버튼을 사용하여 "NowPlayingViewController"를 다시 열려고했습니다.
위임은 여전히 ​​활성화되어 있지만 스트리밍이 진행 중입니다.하지만 노래가 변경되면이보기 컨트롤러의 모든 변수가 업데이트되지만 사용자 인터페이스에 대해 동일한 내용을 말할 수는 없습니다. 디버깅을 시도하고 레이블이 채워지지만 업데이트되지 않은 것으로 나타났습니다. 메인 스레드의 UI 업데이트 및 setNeedDisplay가 도움이되지 않았습니다.
스위프트를 사용하여 라벨이 업데이트되지 않습니다.

NowPlayingViewController

AVPlayer를 설정 :

여기
func setUpPlayer(){ 
     radioPlayer = Player.radio 
     radioPlayer.rate = 1 
     NotificationCenter.default.addObserver(
      self, 
      selector: #selector(self.playerItemDidReachEnd), 
      name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, 
      object: self.radioPlayer.currentItem 
     ) 

    } 

func onMetaData(_ metaData: [AVMetadataItem]?)을 찾을 수 있습니다).

//***************************************************************** 
// MARK: - AVPlayerItem Delegate (for metadata) 
//***************************************************************** 
extension NowPlayingViewController: CustomAVPlayerItemDelegate { 
    func onMetaData(_ metaData: [AVMetadataItem]?) { 
     if let metaDatas = metaData{ 
      startNowPlayingAnimation() 
      let firstMeta: AVMetadataItem = metaDatas.first! 
      let metaData = firstMeta.value as! String 
      var stringParts = [String]() 
      if metaData.range(of: " - ") != nil { 
       stringParts = metaData.components(separatedBy: " - ") 
      } else { 
       stringParts = metaData.components(separatedBy: "-") 
      } 
      // Set artist & songvariables 
      let currentSongName = track.title 
      track.artist = stringParts[0].decodeAllChars() 
      track.title = stringParts[0].decodeAllChars() 
      if stringParts.count > 1 { 
       track.title = stringParts[1].decodeAllChars() 
      }     
      if track.artist == "" && track.title == "" { 
       track.artist = currentStation.stationDesc 
       track.title = currentStation.stationName 
      } 

      DispatchQueue.main.async { 
       if currentSongName != self.track.title { 
        if kDebugLog { 
         print("METADATA artist: \(self.track.artist) | title: \(self.track.title)") 
        } 

        // Update Labels 
        self.artistLabel.text = self.track.artist 
        self.songLabel.text = self.track.title 
        self.updateUserActivityState(self.userActivity!) 
        // songLabel animation 
        self.songLabel.animation = "zoomIn" 
        self.songLabel.duration = 1.5 
        self.songLabel.damping = 1 
        self.songLabel.animate() 
        // Update Stations Screen 
        self.delegate?.songMetaDataDidUpdate(self.track) 
        // Query API for album art 
        self.resetAlbumArtwork() 
        self.queryAlbumArt() 
       } 
      } 
     } 
    } 
} 

이 방법은 timedMetaData 키 경로에 따라 "CustomAVPlayerItem"에서 관찰됩니다. AVPlayer 메타 데이터가 변경 될 때마다 해고됩니다.

import MediaPlayer 

//***************************************************************** 
// This is a singleton struct using Swift 
//***************************************************************** 
struct Player { 
    static var radio = AVPlayer() 
} 

이 내가 "NowPlayingViewController"을 열고 사용하는 SEGUE 기능입니다 :

import MediaPlayer 
import Foundation 

protocol CustomAVPlayerItemDelegate { 
    func onMetaData(_ metaData:[AVMetadataItem]?) 
} 

//***************************************************************** 
// Makes sure that observers are removed before deallocation 
//***************************************************************** 
class CustomAVPlayerItem: AVPlayerItem { 

    var delegate : CustomAVPlayerItemDelegate? 

    init(url URL:URL) 
    { 
     if kDebugLog {print("CustomAVPlayerItem.init")} 
     super.init(asset: AVAsset(url: URL) , automaticallyLoadedAssetKeys:[]) 
     addObserver(self, forKeyPath: "timedMetadata", options: NSKeyValueObservingOptions.new, context: nil) 
    } 

    deinit{   
     if kDebugLog {print("CustomAVPlayerItem.deinit")} 
     removeObserver(self, forKeyPath: "timedMetadata") 
    } 

    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { 
     if let avpItem: AVPlayerItem = object as? AVPlayerItem { 
      if keyPath == "timedMetadata" {     
       delegate?.onMetaData(avpItem.timedMetadata) 
      } 
     } 
    } 
} 

다음은 내 AVPlayer를하다 :이 클래스는 AVPlayerItem의 서브 클래스입니다.
StationsViewController

override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 
     if segue.identifier == "NowPlaying" { 

      self.title = "" 
      firstTime = false 

      let nowPlayingVC = segue.destination as! NowPlayingViewController 
      nowPlayingVC.delegate = self 

      if let indexPath = (sender as? IndexPath) { 
       // User clicked on row, load/reset station 
       if searchController.isActive { 
        currentStation = searchedStations[indexPath.row] 
       } else { 
        currentStation = stations[indexPath.row] 
       } 
       nowPlayingVC.currentStation = currentStation 
       nowPlayingVC.newStation = true 

      } else { 
       // User clicked on a now playing button 
       if let currentTrack = currentTrack { 
        // Return to NowPlaying controller without reloading station 
        nowPlayingVC.track = currentTrack 
        nowPlayingVC.currentStation = currentStation 
        nowPlayingVC.newStation = false 
       } else { 
        // Issue with track, reload station 
        nowPlayingVC.currentStation = currentStation 
        nowPlayingVC.newStation = true 
       } 
      } 
     } 
    } 
+0

'onMetaData'를 호출하는 코드가 없습니다. – matt

+0

내 질문이 업데이트되었습니다. –

+0

왜'onMetaData'만이 문제가된다면 많은 코드를 게시 했습니까? – matt

답변

1

여기 당신이 이해하지 않는 실제로 무슨 일이 일어나고 있는지 생각합니다.

일반적으로 푸시 된보기 컨트롤러에서 "돌아 가기"를하면 밀어 넣기 된보기 컨트롤러는 이 표시되고, 손상된이 파손됩니다. 푸시 된 뷰 컨트롤러는 NowPlayingViewController입니다. 그것에서 당신이 StationsViewController로 "돌아갈"때 파괴되어야합니다. 따라서 NowPlayingViewController 을 다시 표시하려면새롭고 다른 NowPlayingViewController를 만들어야합니다.

좋아요, 지금까지 그렇게 좋았습니다. 모든 것을 이해하면됩니다. 그러나 케이스에는 더 복잡한 문제가 있습니다. 누출이 있습니다! 이전 NowPlayingViewController는 이 아니며이 파괴되었습니다. 따라서 StationsViewController로 돌아가서 NowPlayingViewController를 두 번째로 표시하면 두 개의 NowPlayingViewControllers가 표시됩니다. 사용자가 보는 새 뷰와 새는 뷰가 있습니다.

좋아, 그래서 당신의 로깅은 여전히 ​​관찰하고 업데이트 된 이전 NowPlayingViewController를 표시하고 있습니다.하지만 은 아무것도하지 않고 NowPlayingViewController가 표시됩니다. 그리고 그것은 여러분이 묘사 한 현상을 설명합니다.

만약 이것이 맞다면, 당신이 말했던 것에서는 확신합니다. 그렇다면이 누수가 없도록 아키텍처를 재구성해야합니다. NowPlayingViewController 두 번째로 과 동일한 NowPlayingViewController를 표시합니다. (첫 번째 방법이 더 좋을 것입니다.)

+0

안녕 매트, 정말 당신의 답변을 주셔서 감사하고 싶습니다! 네가 한 말 이해해. 시뮬레이터에서 내 응용 프로그램 뷰 계층 구조를 디버그하려고했는데 NowPlayingViewController가 제대로 파괴 된 것 같습니다. 여기 몇 가지 이미지 : 개봉 (https://www.dropbox.com/s/xam343xrt3abbwk/1st_time.png?dl=0), 돌아오고 돌아 오는 (https://www.dropbox.com/s/5y9byuw9zujhide/) 후 2nd_time.png? dl = 0). –