【问题标题】:Label does not update using Swift标签不使用 Swift 更新
【发布时间】:2016-12-22 00:28:19
【问题描述】:

我正在尝试改进我分叉的 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 元数据更改时都会触发它。该类是 AVPlayerItem 的子类:

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:

import MediaPlayer

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

这是我用来打开“NowPlayingViewController”的segue函数。
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
                }
            }
        }
    }

【问题讨论】:

  • 我没有看到任何调用onMetaData的代码。
  • 我更新了我的问题。
  • 如果只有onMetaData 有问题,你为什么要在这里发布这么多代码?
  • 提供了更多信息。谢谢你的回答。
  • 你可以尝试在 dipatch Main traililng 闭包中运行标签更新吗?

标签: ios swift user-interface uilabel avplayer


【解决方案1】:

以下是我认为您不了解的内容以及实际发生的情况。

通常,当您从推送的视图控制器“返回”时,推送的视图控制器会弹出并销毁。您推送的视图控制器是 NowPlayingViewController。当您从它“返回”到 StationsViewController 时,它应该被销毁。因此,当您再次显示 NowPlayingViewController 时,您必须创建一个新的、不同的NowPlayingViewController。

好的,到目前为止一切顺利,前提是您了解所有这些。但是在你的的情况下还有一个更复杂的情况:你有泄漏!您的旧 NowPlayingViewController 没有被销毁。因此,当您“返回”到 StationsViewController 并再次显示 NowPlayingViewController 时,现在有 两个 NowPlayingViewController — 您看到的新的和正在泄漏的旧的。 p>

好的,所以您的 logging 继续显示 old NowPlayingViewController,它仍在观察和更新。但是你的眼睛正在看到new NowPlayingViewController,它什么也没做。这就解释了你所描述的现象。

如果这是正确的——而且,根据你所说的,我很确定它是正确的——那么你需要重新组织你的架构,这样你就不会出现这种泄漏,或者当你显示 NowPlayingViewController 时第二次显示 same NowPlayingViewController 而不是创建一个不同的。 (第一种方法会更好。)

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多