第一篇博客❤️
作者的小唠叨:学了两个月,终于实现了我的天气,很开心可以来搞iOS开发,组里的每个人都特别好氛围也很好!这感觉就像回家了一样!
1、获取定位(CoreLocation获得经纬度)
2、网络请求(HTTP请求天气API)
3、下拉刷新数据
4、缓存数据
5、基本控件的实现(CollectionView、TableView等)
最终得到的天气
这里我们要用到第三方库CoreLocation
关于导入第三方库可以看这篇文章
首先导入CoreLocation:
import CoreLocation
CoreLocation用Location Manger获取用户的位置,在这之前要先在info.plist中加入定位描述。
iOS 11之后设置:
Privacy - Location When In Use Usage Description //只允许使用应用期间APP获取地理位置
Privacy - Location Always Usage Description //一直在后台获取更新的定位信息
我用的是第一个,因为相对于第二个,第一个会节省手机的电量
创建CLLocationManager对象并设置代理
let locationManager = CLLocationManager()//定位管理器
locationManager.delegate = self
locationManager.requestWhenInUseAuthorization()//根据info.plist来
locationManager.desiredAccuracy = kCLLocationAccuracyHundredMeters//设置精度
locationManager.requestLocation()//发送授权申请
locationManager.startUpdatingLocation()//开启定位服务更新
获取地理位置和错误处理
extension MainViewController: CLLocationManagerDelegate{
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[locations.count - 1]
if(location.horizontalAccuracy > 0) {
self.locationManager.stopUpdatingHeading()
let longitude = String(location.coordinate.longitude)//获得经度
let latitude = String(location.coordinate.latitude)//获得纬度
//发起http请求天气
let parms = ["lat":String(latitude),"lon":String(longitude),"appid":myKey]
self.requestWeather(parms: parms)
}
}
//错误处理
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error")
}
}
以上就可以实现定位功能
运用HTTP请求天气站台的API,得到返回的JSON数据,解析之后更新到界面中。
我用的是https://api.openweathermap.org
⚠️在请求前要获得你的key
请求的URL:https://api.openweathermap.org/data/2.5/weather
最后得到
let weatherApi = "https://api.openweathermap.org/data/2.5/weather"
let myKey = "713e52a79d1597d9d9e1281c26feeb49"//每个人的key是不一样的,一定要获取
请求之前,先导入Alamofire、SwiftyJSON、HandyJSON等,这里要在podfile里更改
source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '9.0'
inhibit_all_warnings!
target 'Weather' do
use_frameworks!
# Pods for Weather
pod 'SnapKit'
pod "ESTabBarController-swift"
pod 'EachNavigationBar'
pod 'DNSPageView'
pod 'HandyJSON', :git => 'https://github.com/alibaba/HandyJSON.git', :branch => 'dev_for_swift5.0'
pod 'SwiftyJSON'
pod 'Alamofire'
pod 'AlamofireImage'
pod 'AlamofireObjectMapper'
pod 'SwiftFCXRefresh'
end
更改后在ViewController里导入(与导入CoreLocation相似)
用第三方库Alamofire请求,responseJSON 序列化为JSON
Alamofire在默认状态下以GET(从服务器上获取数据)方式进行网络请求,其他还有POST(向服务器传达数据)、PUT、DELETE等。具体可以了解一下URLSession进行网络请求。
(结果会通过闭包的参数response返回。如果是被序列化的数据,就通过response中的 .value来获取数据)
func requestWeather(parms: Parameters){
if getLocalData() == nil {//缓存数据
Alamofire.request(weatherApi,parameters: parms).responseJSON { [self]
response in
if let json = response.value {
let weatherJSON = JSON(json)
self.Data = JSONDeserializer<WeatherData>.deserializeFrom(json: weatherJSON.description)!
putdata(data: Data)//缓存
collectionView.reloadData()//重新加载collectionView
}
}
}else{
Data = getLocalData()!
collectionView.reloadData()
}
}
在Alamofire中,对请求的封装有以下几种类型
response.request//original URL request
response.response//HTTP URL response
response.data//server data
response.result//result of response serialization
如果一切正确,你会得到
{
"name" : "Chengdu",
"visibility" : 10000,
"timezone" : 28800,
"cod" : 200,
"wind" : {
"deg" : 0,
"speed" : 1
},
"base" : "stations",
"dt" : 1637844838,
"sys" : {
"type" : 1,
"id" : 9674,
"sunset" : 1637834593,
"country" : "CN",
"sunrise" : 1637797110
},
"coord" : {
"lat" : 30.666699999999999,
"lon" : 104.0667
},
"clouds" : {
"all" : 40
},
"weather" : [
{
"description" : "scattered clouds",
"id" : 802,
"icon" : "03n",
"main" : "Clouds"
}
],
"main" : {
"humidity" : 58,
"pressure" : 1023,
"temp" : 287.08999999999997,
"feels_like" : 286.05000000000001,
"temp_max" : 287.08999999999997,
"temp_min" : 287.08999999999997
},
"id" : 1815286
}
这只是一个例子,得到这样的数据之后是要进行解析的,解析后每一层都需要配备一个实体类。
比如此时,我得到的Model是这样的
import HandyJSON
struct WeatherData: HandyJSON {
let coord = Coord()
let weather = [Weather]()
let base = ""
var main = Main()
let visibility = ""
let wind = Wind()
let cloud = Clouds()
let dt = ""
let sys = Sys()
let timezone = ""
let id = ""
let name = ""
let cod = ""
}
struct Coord: HandyJSON {
let lon = ""
let lat = ""
}
struct Weather: HandyJSON {
let id = ""
let main = ""
let description = ""
let icon = ""
}
struct Main: HandyJSON {
var temp = 0
let feels_like = 0
let temp_min = 0
let temp_max = 0
let pressure = ""
let humidity = ""
let sea_leavel = ""
let grnd_level = ""
}
struct Wind: HandyJSON {
let speed = ""
let deg = ""
let gust = ""
}
struct Clouds: HandyJSON {
let all = ""
}
struct Sys: HandyJSON {
let country = ""
let sunrise = ""
let sunset = ""
}
这样MVC模式下的Model也创建好了
由此,网络请求功能完成!
用第三方库SwiftFCXRefresh实现下拉刷新
先使用CocoaPods导入podfile中,在写在头文件里(与前面相同,这里不多赘述)
var headerRefreshView: FCXRefreshHeaderView?
//自动刷新功能
navigationItem.rightBarButtonItem = UIBarButtonItem.init(barButtonSystemItem: .refresh, target: self, action: #selector(autoRefresh))
@objc func autoRefresh() {
headerRefreshView?.autoRefresh()
self.collectionView.reloadData()
}
//下拉刷新功能
var headerRefreshView: FCXRefreshHeaderView?
func addRefreshView() {//在viewDidLoad调用
headerRefreshView = collectionView.addFCXRefreshHeader {(refreshHeader) in
DispatchQueue.main.asyncAfter(deadline: .now() + 0.7) {
refreshHeader.endRefresh()
self.collectionView.reloadData()
}}
}
显示效果
学习缓存数据之前我们先学习一个概念:序列化
自定义对象是无法写入到文件的,必须转换成二进制流的格式。从对象到二进制数据的过程我们称为序列化,将二进制数据还原成对象的过程,我们称为反序列化。iOS中对象序列化需要实现NSCoding类协议,实现序列化与反序列化方法
func getLocalData() -> WeatherData? {
let path = NSHomeDirectory().appending("/Documents/user.plist")//文件位置
let data = NSArray(contentsOfFile: path)//文件内容
let dic = data?[0] as? NSDictionary//转成NSDictionary
let model = WeatherData.deserialize(from: dic, designatedPath: "")//反序列化
return model
}
func putdata(data: WeatherData) {
let dic = NSDictionary(dictionary: data.toJSON()!)
let path = NSHomeDirectory().appending("/Documents/user.plist")//文件位置
let arr = NSMutableArray() //可变数组
arr.add(dic) //增加元素
arr.write(toFile: path, atomically: true)//将数组写入plist文件
}
注意:网络请求之前要判断本地数据是否为nil,如果是则进行网络请求
网络请求后要重写本地数据
func requestWeather(parms: Parameters){
//判断本地数据
if getLocalData() == nil {
Alamofire.request(weatherApi,parameters: parms).responseJSON { [self]
response in
if let json = response.value {
let weatherJSON = JSON(json)
self.Data = JSONDeserializer<WeatherData>.deserializeFrom(json: weatherJSON.description)!
//重写本地数据
putdata(data: Data)
collectionView.reloadData()
}
}
}else{
Data = getLocalData()!
collectionView.reloadData()
}
}
基本控件就是一些UICollectionView、UITableView、Label、UIImageView等
在主界面运用UICollectionView来将搜索、温度、详情分成三个cell来写,点进搜索后运用了UITableView来选择城市
CollectionViewLayout里有函数可以设置cell的大小和间距
设置cell的大小
func collectionView(_ collectionView: UICollectionView,layout collectonViewLayout: UICollectionViewLayout,sizeForItemAt indexPath: IndexPath) -> CGSize{
return CGSize(width: 100, height: 100)
}
设置cell的间距
横向 minimumLineSpacing
纵向 minimumInteritemSpacing
举个例子当我 layout.scrollDirection = .horizontal(横向)时
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat {
return 24
}
运用SnapKit画UI,很方便
举个例子
nameLabel.snp.makeConstraints { make in
make.top.equalToSuperview().offset(80)
make.left.equalToSuperview().offset(40)
make.width.equalTo(200)
make.height.equalTo(50)
}
cloudLabel.snp.makeConstraints { make in
make.top.equalTo(nameLabel.snp.bottom).offset(20)
make.left.equalToSuperview().offset(40)
make.right.equalToSuperview().offset(0)
make.height.equalTo(30)
}
以上就是我这两个月的学习总结,当然不止这些还有一些动画啊模糊效果这些后续会有博客介绍❤️