《DAY 30》天气 App 实作(完)

最後一天来把天气 App 的剩余部分给完成,冲啊!

  • 当一开始进入 App 会直接显示宜兰县在第一个时间区间的天气资讯。
    https://ithelp.ithome.com.tw/upload/images/20201013/20129680sgUY2eur5m.png

  • 点击一下画面可以选择地点,因此要把选取器放入提示框里。
    https://ithelp.ithome.com.tw/upload/images/20201013/20129680TUadaBrs1z.png

  • 选完地点後接着就让使用者选择时间。
    https://ithelp.ithome.com.tw/upload/images/20201013/20129680JVqLlYX5t0.png

  • 完整的程序码如下,看起来有点恐怖,但如果清楚流程其实还好。

import UIKit

class ViewController: UIViewController {
    
    let location = ["宜兰县","花莲县","台东县","澎湖县","金门县","连江县","台北市","新北市","桃园市","台中市","台南市","高雄市","基隆市","新竹县","新竹市","苗栗县","彰化县","南投县","云林县","嘉义县","嘉义市","屏东县"]
    var selectLocation = "宜兰县" // 选取器选择的地点
    var selectTime = 0 // 选取器选择的时间区间,范围 0~2
    var timeData = ["","",""] // 读取 JSON 後,存放三个时间的 startTime
    let locationPickerView = UIPickerView(frame: CGRect(x: 0, y: 50, width: 270, height: 150)) // 把地点选取器放入提示框里,大小要自己尝试调整
    let timePickerView = UIPickerView(frame: CGRect(x: 0, y: 50, width: 270, height: 150)) // 把时间选取器放入提示框里,大小要自己尝试调整
    
    @IBOutlet weak var locationName: UILabel!
    @IBOutlet weak var Wx: UILabel!
    @IBOutlet weak var PoP: UILabel!
    @IBOutlet weak var MinT: UILabel!
    @IBOutlet weak var CI: UILabel!
    @IBOutlet weak var MaxT: UILabel!
    @IBOutlet weak var startTime: UILabel!
    @IBOutlet weak var endTime: UILabel!
    
    @IBOutlet weak var weatherIcon: UIImageView!
    
        
    override func viewDidLoad() {
        super.viewDidLoad()
        
        locationPickerView.dataSource = self
        locationPickerView.delegate = self
        timePickerView.dataSource = self
        timePickerView.delegate = self
        
        loadJSON(locationName: selectLocation, time: selectTime) // 进入画面前先读一次 JSON,以便得到 timeData
        
    }
    
    // MARK:- 读 JSON 资料,必须要传入地点和时间
    func loadJSON(locationName: String ,time: Int){
        
        let url =  "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-C0032-001?Authorization=CWB-6F803E34-66E5-4135-AC7D-25811AD53D5C&format=JSON&locationName=\(locationName)"
        let newUrl = url.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)! // 网址有中文,需要先编码
        
        var request = URLRequest(url: URL(string: newUrl)!,timeoutInterval: Double.infinity)
        request.httpMethod = "GET"
        let task = URLSession.shared.dataTask(with: request){(data, respond, error) in
            
            let decoder = JSONDecoder()
            if let data = data, let weather = try? decoder.decode(Weather.self, from: data){
                
                print(weather)
                
                DispatchQueue.main.sync {
                    
                    self.locationName.text = weather.records.location[0].locationName
                    self.Wx.text = weather.records.location[0].weatherElement[0].time[time].parameter.parameterName
                    self.PoP.text = weather.records.location[0].weatherElement[1].time[time].parameter.parameterName + "%"
                    self.MinT.text = weather.records.location[0].weatherElement[2].time[time].parameter.parameterName + "°" + weather.records.location[0].weatherElement[2].time[time].parameter.parameterUnit!
                    self.CI.text = weather.records.location[0].weatherElement[3].time[time].parameter.parameterName
                    self.MaxT.text = weather.records.location[0].weatherElement[4].time[time].parameter.parameterName + "°" + weather.records.location[0].weatherElement[4].time[time].parameter.parameterUnit!
                    self.startTime.text = weather.records.location[0].weatherElement[0].time[time].startTime
                    self.endTime.text = weather.records.location[0].weatherElement[0].time[time].endTime
                    
                    for i in 0...2 {
                        self.timeData[i] = weather.records.location[0].weatherElement[0].time[i].startTime
                    } // 把三个 startTime 放入阵列
                    
                    self.changeWeatherIcon() // 根据天气现象改变图示

                }
                
            }
            else {
                print("error")
            }
            
        }
        task.resume()
    }
    
    // MARK:- 改变 weatherIcon
    func changeWeatherIcon() {
        if Wx.text!.contains("积冰") {
            weatherIcon.image = UIImage.init(systemName: "snow")
        }
        else if Wx.text!.contains("暴风雪"){
            weatherIcon.image = UIImage.init(systemName: "wind.snow")
        }
        else if Wx.text!.contains("雪") {
            if Wx.text!.contains("雨") {
                weatherIcon.image = UIImage.init(systemName: "cloud.sleet")
            }
            else {
                weatherIcon.image = UIImage.init(systemName: "cloud.snow")
            }
        }
        else if Wx.text!.contains("雨") {
            if Wx.text!.contains("雷") {
                weatherIcon.image = UIImage.init(systemName: "cloud.bolt.rain")
            }
            else if Wx.text!.contains("晴"){
                weatherIcon.image = UIImage.init(systemName: "cloud.sun.rain")
            }
                
            else {
                weatherIcon.image = UIImage.init(systemName: "cloud.rain")
            }
        }
        else if Wx.text!.contains("晴") {
            if Wx.text!.contains("云") {
                weatherIcon.image = UIImage.init(systemName: "cloud.sun")
            }
            else {
                weatherIcon.image = UIImage.init(systemName: "sun.max")
            }
        }
        else if Wx.text!.contains("阴") {
            weatherIcon.image = UIImage.init(systemName: "smoke.fill")
        }
        else if Wx.text!.contains("云") {
            weatherIcon.image = UIImage.init(systemName: "smoke")
        }
        
    }
    
    
    // MARK:- 点击一下画面依序跳出选取器
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        locationView() // 点击一下画面可以选择地点
    }
    
    func locationView() {
        let alertView = UIAlertController(
            title: "选择地点",
            message: "\n\n\n\n\n\n\n\n\n", // 因为要放入选取器,使提示框高度增高
            preferredStyle: .alert
        )
        let cancelAction = UIAlertAction(
            title: "取消",
            style: .default,
            handler: nil
        )
        
        let okAction = UIAlertAction(
            title: "确认",
            style: .destructive,
            handler: {_ in
                self.loadJSON(locationName: self.selectLocation, time: self.selectTime)
                self.timeView()} // 选完地点接着选时间
        )
        
        alertView.view.addSubview(locationPickerView)
        alertView.addAction(cancelAction)
        alertView.addAction(okAction)
        
        present(alertView, animated: true, completion: nil)
    }
    func timeView() {
        let alertView = UIAlertController(
            title: "选择时间",
            message: "\n\n\n\n\n\n\n\n\n", // 因为要放入选取器,使提示框高度增高
            preferredStyle: .alert
        )
        let cancelAction = UIAlertAction(
            title: "取消",
            style: .default,
            handler: nil
        )
        
        let okAction = UIAlertAction(
            title: "确认",
            style: .destructive,
            handler: {_ in self.loadJSON(locationName: self.selectLocation, time: self.selectTime)}
        )
        
        alertView.view.addSubview(timePickerView)
        alertView.addAction(cancelAction)
        alertView.addAction(okAction)
        
        present(alertView, animated: true, completion: nil)
    }
    
}

    // MARK:- 选取器

extension ViewController: UIPickerViewDataSource, UIPickerViewDelegate {
    
    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }
    
    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        
        if pickerView == locationPickerView {
            return location.count
        }
        if pickerView == timePickerView {
            return timeData.count // 三个时间区间
        }

        return 0
        
    }
    
    func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
        if pickerView == locationPickerView {
            return location[row]
        }
        if pickerView == timePickerView {
            return timeData[row]
        }

        return nil
    }


    func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
        
        if pickerView == locationPickerView {
            selectLocation = location[row] // 改变地点
        }
        if pickerView == timePickerView {
            selectTime = row // 改变时间
        }
        
    }
    
}
  • 执行起来的样子。

心得:终於完赛了,这些天介绍了几个元件,也做了 1A2B 小游戏和这个天气 App,其实开赛前我本来预计还要做其它 App 出来的,但是那时候硬碟坏轨,导致许多写好的程序都不见,所以这 30 天来几乎是一天写一篇,写好程序再来写文章,即使是写过的还必须再写一次,实在是颇累人的,但是往好的方面想,这样可以让自己写过的东西记忆更深,即使忘掉了只要过来这里复制就好,也在这里看到了团队队友做过,而我却没做过的作品。


<<:  [Day 28] Fortinet NSE Institute 免费网路安全培训课程

>>:  Day29_渗透 thc-hydra

Day 30 完赛!!!!!

太太太感人了,中间几度想弃赛,这次中秋连假跟国庆连假估计让完赛人数下降不少,放假还要发文直比酷刑,而...

大共享时代系列_028_云端串流游戏 ( Cloud Gaming )

云端串流游戏加速推坑的开始~ (∩^o^)⊃━☆゚.*・。 线上游戏跟云端串流游戏的差异? 线上游戏...

C#入门之异步编程

在很多情况下,我们的任务前后之间没有必然的联系的,所以我们可以不需要等待前面命令结束,才开始后面的任...

Day 04 - Credit Enquires 与 Short Stock Source

本篇重点 Credit Enquires Short Stock Source Pandas Dat...

day16 : NATS 、NATS Streaming、JetStream服务应用 on K8S (下)

昨天简略的介绍了今天会布署的NATS,虽然NATS Streaming要被弃用了,不过我还是会介绍一...