【问题标题】:Using different URL links for JSON Tableview对 JSON Tableview 使用不同的 URL 链接
【发布时间】:2018-09-13 08:19:00
【问题描述】:

我要做的是根据我的应用程序中发生的情况从不同的链接(4 个不同的链接)加载我的 JSON。这是一个具有 4 个不同广播电台的广播网络应用程序。我可以将当​​前正在播放的电台推送到我的 JSON ViewController,但问题是如何让 JSON 执行“如果当前电台正在播放,则从此链接拉取:链接到 JSON 信息”?我知道这是可能的,但不知道如何实施。这是我的 JSON TableviewViewController 代码:

    import UIKit

//----------
//MARK: JSON
//----------

//The Initial Response From The JSON
struct Response: Codable {

    var playHistory: Album

}

//The Album Received Which Is An Array Of Song Data
struct Album: Codable {
    var song: [SongData]

}

//The SongData From The PlayHistory Album
struct SongData: Codable{

    var album: String
    var artist: String
    var cover: String
    var duration: String
    var programStartTS: String
    var title: String
}


class TableViewController: UITableViewController {

    //1. Create An Array To Store The SongData
    var songs = [SongData]()
    var currentStation: RadioStation!
    var downloadTask: URLSessionDownloadTask?

    override func viewDidLoad() { super.viewDidLoad()


        self.tableView.delegate = self
        self.tableView.dataSource = self

        //2. Load The JSON From The Main Bundle

        guard let urlText = URL (string: currentStation.longDesc)
            else { return }


        do{
            //a. Get The Data From The From The File
            let data = try Data(contentsOf: urlText)

            //b. Decode The Data To Our Structs
            let albumData = try JSONDecoder().decode(Response.self, from: data)

            //c. Append The Songs Array With The PlayHistory
            albumData.playHistory.song.forEach { songs.append($0) }

            //d. Test Some Data
            print("""
                **The First Album Details**
                Album = \(songs[0].album)
                Artist = \(songs[0].artist)
                Cover = \(songs[0].cover)
                Duration = \(songs[0].duration)
                Start = \(songs[0].programStartTS)
                Title = \(songs[0].title)
                """)

            //3. Load The Data
            DispatchQueue.main.async {
                self.tableView.reloadData()
            }
        }catch{

            print(error)
        }

    }

    //-----------------
    //MARK: UITableView
    //-----------------


    override func numberOfSections(in tableView: UITableView) -> Int {
        return 1
    }

    override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return songs.count
    }


    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {


        //1. Create A Cell
        let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell

        //2. Set It's Text
        cell.songTitle.text = songs[indexPath.row].title
        cell.artistLabel.text = songs[indexPath.row].artist

        //3. Get The Image
        if let imageURL = URL(string: songs[indexPath.row].cover){

            let request = URLSession.shared.dataTask(with: imageURL) { (imageData, response, error) in

                if let error = error{

                    print(error)

                }else{

                    guard let image = imageData else { return }

                    DispatchQueue.main.async {
                        cell.songCover.image = UIImage(data: image)
                        cell.setNeedsLayout()
                        cell.layoutIfNeeded()
                    }

                }
            }


            request.resume()
        }

        return cell

    }


    override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {

        print("""
            **Album \(indexPath.row) Selected**
            Album = \(songs[indexPath.row].album)
            Artist = \(songs[indexPath.row].artist)
            Cover = \(songs[indexPath.row].cover)
            Duration = \(songs[indexPath.row].duration)
            Start = \(songs[indexPath.row].programStartTS)
            Title = \(songs[indexPath.row].title)
            """)
    }

}

这是我的“正在播放 ViewController”中的代码:

import UIKit
import MediaPlayer

//*****************************************************************
// NowPlayingViewControllerDelegate
//*****************************************************************

protocol NowPlayingViewControllerDelegate: class {
    func didPressPlayingButton()
    func didPressStopButton()
    func didPressNextButton()
    func didPressPreviousButton()
}

//*****************************************************************
// NowPlayingViewController
//*****************************************************************

class NowPlayingViewController: UIViewController {

    weak var delegate: NowPlayingViewControllerDelegate?

    // MARK: - IB UI

    @IBOutlet weak var albumHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var albumImageView: SpringImageView!
    @IBOutlet weak var artistLabel: UILabel!
    @IBOutlet weak var playingButton: UIButton!
    @IBOutlet weak var songLabel: SpringLabel!
    @IBOutlet weak var stationDescLabel: UILabel!
    @IBOutlet weak var volumeParentView: UIView!
    @IBOutlet weak var previousButton: UIButton!
    @IBOutlet weak var nextButton: UIButton!
    @IBOutlet weak var recentlyPlayed: UIButton!

    // MARK: - Properties

    var currentStation: RadioStation!
    var currentTrack: Track!

    var newStation = true
    var nowPlayingImageView: UIImageView!
    let radioPlayer = FRadioPlayer.shared

    var mpVolumeSlider: UISlider?

    //*****************************************************************
    // MARK: - ViewDidLoad
    //*****************************************************************

    override func viewDidLoad() {
        super.viewDidLoad()

        // Create Now Playing BarItem
        createNowPlayingAnimation()

        // Set AlbumArtwork Constraints
        optimizeForDeviceSize()

        // Set View Title
        self.title = currentStation.name


        // Set UI
        albumImageView.image = currentTrack.artworkImage
        stationDescLabel.text = currentStation.desc
        stationDescLabel.isHidden = currentTrack.artworkLoaded

        // Check for station change
        newStation ? stationDidChange() : playerStateDidChange(radioPlayer.state, animate: false)

        // Setup volumeSlider
        setupVolumeSlider()

        // Hide / Show Next/Previous buttons
        previousButton.isHidden = hideNextPreviousButtons
        nextButton.isHidden = hideNextPreviousButtons
    }

    //*****************************************************************
    // MARK: - Setup
    //*****************************************************************

    func setupVolumeSlider() {
        // Note: This slider implementation uses a MPVolumeView
        // The volume slider only works in devices, not the simulator.
        for subview in MPVolumeView().subviews {
            guard let volumeSlider = subview as? UISlider else { continue }
            mpVolumeSlider = volumeSlider
        }

        guard let mpVolumeSlider = mpVolumeSlider else { return }

        volumeParentView.addSubview(mpVolumeSlider)

        mpVolumeSlider.translatesAutoresizingMaskIntoConstraints = false
        mpVolumeSlider.leftAnchor.constraint(equalTo: volumeParentView.leftAnchor).isActive = true
        mpVolumeSlider.rightAnchor.constraint(equalTo: volumeParentView.rightAnchor).isActive = true
        mpVolumeSlider.centerYAnchor.constraint(equalTo: volumeParentView.centerYAnchor).isActive = true

        mpVolumeSlider.setThumbImage(#imageLiteral(resourceName: "slider-ball"), for: .normal)
    }

    func stationDidChange() {
        radioPlayer.radioURL = URL(string: currentStation.streamURL)
        title = currentStation.name
    }

    //*****************************************************************
    // MARK: - Player Controls (Play/Pause/Volume)
    //*****************************************************************

    // Actions

    @IBAction func playingPressed(_ sender: Any) {
        delegate?.didPressPlayingButton()
    }

    @IBAction func stopPressed(_ sender: Any) {
        delegate?.didPressStopButton()
    }

    @IBAction func nextPressed(_ sender: Any) {
        delegate?.didPressNextButton()
    }

    @IBAction func previousPressed(_ sender: Any) {
        delegate?.didPressPreviousButton()
    }

    //*****************************************************************
    // MARK: - Load station/track
    //*****************************************************************

    func load(station: RadioStation?, track: Track?, isNewStation: Bool = true) {
        guard let station = station else { return }

        currentStation = station
        currentTrack = track
        newStation = isNewStation
    }

    func updateTrackMetadata(with track: Track?) {
        guard let track = track else { return }

        currentTrack.artist = track.artist
        currentTrack.title = track.title

        updateLabels()
    }

    // Update track with new artwork
    func updateTrackArtwork(with track: Track?) {
        guard let track = track else { return }

        // Update track struct
        currentTrack.artworkImage = track.artworkImage
        currentTrack.artworkLoaded = track.artworkLoaded

        albumImageView.image = currentTrack.artworkImage

        if track.artworkLoaded {
            // Animate artwork
            albumImageView.animation = "wobble"
            albumImageView.duration = 3
            albumImageView.animate()
            stationDescLabel.isHidden = true
        } else {
            stationDescLabel.isHidden = false
        }

        // Force app to update display
        view.setNeedsDisplay()
    }

    private func isPlayingDidChange(_ isPlaying: Bool) {
        playingButton.isSelected = isPlaying
        startNowPlayingAnimation(isPlaying)
    }

    func playbackStateDidChange(_ playbackState: FRadioPlaybackState, animate: Bool) {

        let message: String?

        switch playbackState {
        case .paused:
            message = "Station Paused..."
        case .playing:
            message = nil
        case .stopped:
            message = "Station Stopped..."
        }

        updateLabels(with: message, animate: animate)
        isPlayingDidChange(radioPlayer.isPlaying)
    }

    func playerStateDidChange(_ state: FRadioPlayerState, animate: Bool) {

        let message: String?

        switch state {
        case .loading:
            message = "Loading Station ..."
        case .urlNotSet:
            message = "Station URL not valide"
        case .readyToPlay, .loadingFinished:
            playbackStateDidChange(radioPlayer.playbackState, animate: animate)
            return
        case .error:
            message = "Error Playing"
        }

        updateLabels(with: message, animate: animate)
    }

    //*****************************************************************
    // MARK: - UI Helper Methods
    //*****************************************************************

    func optimizeForDeviceSize() {

        // Adjust album size to fit iPhone 4s, 6s & 6s+
        let deviceHeight = self.view.bounds.height

        if deviceHeight == 480 {
            albumHeightConstraint.constant = 106
            view.updateConstraints()
        } else if deviceHeight == 667 {
            albumHeightConstraint.constant = 230
            view.updateConstraints()
        } else if deviceHeight > 667 {
            albumHeightConstraint.constant = 260
            view.updateConstraints()
        }
    }

    func updateLabels(with statusMessage: String? = nil, animate: Bool = true) {

        guard let statusMessage = statusMessage else {
            // Radio is (hopefully) streaming properly
            songLabel.text = currentTrack.title
            artistLabel.text = currentTrack.artist
            shouldAnimateSongLabel(animate)
            return
        }

        // There's a an interruption or pause in the audio queue

        // Update UI only when it's not aleary updated
        guard songLabel.text != statusMessage else { return }

        songLabel.text = statusMessage
        artistLabel.text = currentStation.name

        if animate {
            songLabel.animation = "flash"
            songLabel.repeatCount = 3
            songLabel.animate()
        }
    }

    // Animations

    func shouldAnimateSongLabel(_ animate: Bool) {
        // Animate if the Track has album metadata
        guard animate, currentTrack.title != currentStation.name else { return }

        // songLabel animation
        songLabel.animation = "zoomIn"
        songLabel.duration = 1.5
        songLabel.damping = 1
        songLabel.animate()
    }

    func createNowPlayingAnimation() {

        // Setup ImageView
        nowPlayingImageView = UIImageView(image: UIImage(named: "NowPlayingBars-3"))
        nowPlayingImageView.autoresizingMask = []
        nowPlayingImageView.contentMode = UIViewContentMode.center

        // Create Animation
       // nowPlayingImageView.animationImages = AnimationFrames.createFrames()
       // nowPlayingImageView.animationDuration = 0.7

        // Create Top BarButton
        let barButton = UIButton(type: .custom)
        barButton.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
        barButton.addSubview(nowPlayingImageView)
        nowPlayingImageView.center = barButton.center

        //let barItem = UIBarButtonItem(customView: barButton)
        //self.navigationItem.rightBarButtonItem = barItem
    }

    func startNowPlayingAnimation(_ animate: Bool) {
        animate ? nowPlayingImageView.startAnimating() : nowPlayingImageView.stopAnimating()
    }

    //*****************************************************************
    // MARK: - Segue
    //*****************************************************************





    @IBAction func shareButtonPressed(_ sender: UIButton) {
        let songToShare = "I'm listening to \(currentTrack.title) by: \(currentTrack.artist) on \(currentStation.name)"
        let activityViewController = UIActivityViewController(activityItems: [songToShare, currentTrack.artworkImage!], applicationActivities: nil)
        activityViewController.completionWithItemsHandler = {(activityType: UIActivityType?, completed:Bool, returnedItems:[Any]?, error: Error?) in
            if completed {
                    // do something on completion if you want
                            }
                    }
        present(activityViewController, animated: true, completion: nil)
    }
}

【问题讨论】:

    标签: ios json swift tableview


    【解决方案1】:

    对于不同的广播电台,您有不同的链接,然后创建一个 var currentStationUrl 来存储该特定电台的链接,如果 URL 已更新,则调用您的 API 以获取与该链接对应的更新数据

    var currentStationUrl: String!{
            didSet{
                loadData(urlString: self.currentStationUrl) { (album, status) in
                    if status == true{
                        // Do your stuff with response data here
                        print(album?.playHistory.song[0].title)
                    }else{
                        print("Can't get data from radio station")
                    }
                }
            }
        }
    

    当 RadioStation 发生变化时更新RadioStation 的链接。

        var currentStation: RadioStation!{
            didSet{
                if self.currentStation == YOUR_RADIO_STATION_1{
                    self.currentStationUrl = "http://streamdb3web.securenetsystems.net/player_status_update/JACKSON1_history.txt"
                }
                else if self.currentStation == YOUR_RADIO_STATION_2{
                    self.currentStationUrl = LINK_FOR_RADIO_STATION_2
                }
                else if self.currentStation == YOUR_RADIO_STATION_3{
                    self.currentStationUrl = LINK_FOR_RADIO_STATION_3
                }
                else if self.currentStation ==YOUR_RADIO_STATION_4{
                    self.currentStationUrl = LINK_FOR_RADIO_STATION_4
                }
            }
        }
    

    最后一个重要的任务是调用广播电台的链接 (API),以单独的方法执行,以便您可以重复使用它。

    func loadData(urlString: String,completion: @escaping (Response?, Bool ) -> ()) {
    
            guard let url = URL(string: urlString) else {
                completion(nil, false)
                return
            }
    
            URLSession.shared.dataTask(with: url) { (data, response, error) in
                if error != nil {
                    print(error!.localizedDescription)
                    completion(nil, false)
                }
                guard let data = data else {
                    completion(nil, false)
                    return }
                do {
                    let albumData = try JSONDecoder().decode(Response.self, from: data)
                    completion(albumData, true)
    
                } catch let jsonError {
                    completion(nil, false)
                    print(jsonError)
                }
                }.resume()
        }
    

    要从新站获取更新数据,只需更新 currentStation 的值。

    self.currentStation == YOUR_RADIO_STATION_1
    

    【讨论】:

    • 这看起来可行,但我有点困惑,不确定如何在不破坏代码的情况下将其实现到我的代码中。我还是个新手,所以请帮忙。提前致谢。
    • 你不需要做更多的事情,只需按照给定的说明复制并粘贴上面的代码。
    • 在你的TableViewController类中添加上面的代码。
    • 我会再试几次并报告。谢谢。
    • 您要更改广播电台的位置,也粘贴该方法的代码。
    【解决方案2】:

    您需要将currentStation 传递给TableViewController

    您说故事板中有一个segue,从NowPlayingViewControllerTableViewController

    在这种情况下,您需要在prepare(for segue:sender:) 中的NowPlayingViewController 中执行以下操作:

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if let vc = segue.destination as? TableViewController {
            vc.currentStation = currentStation
        }
    }
    

    【讨论】:

    • 太棒了!谢谢先生。
    • 所以我添加了这一点,但我一直收到此错误:由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无法创建类“(null)”的segue
    • @ManuelDuarte 你确定你将 segue 命名为“recents”吗?
    • @ManuelDuarte 不管怎样,顺其自然吧。随便命名,但尝试更新答案
    • 它似乎仍然有那个错误。 Segue 被正确命名。错误出现在带有线程 1 的 AppDelegate 类中:信号 SIGABRT。
    猜你喜欢
    • 2015-01-18
    • 1970-01-01
    • 1970-01-01
    • 2017-06-08
    • 1970-01-01
    • 2015-01-28
    • 1970-01-01
    • 2011-11-25
    • 1970-01-01
    相关资源
    最近更新 更多