首页

动手练一练,做一个简单的天气预报应用(weather app three)【下】

By 前端达人
Published in D-案例练习
August 11, 2022
1 min read
动手练一练,做一个简单的天�气预报应用(weather app three)【下】

在本系列的第一节中,我们学习了如何获取用户的位置坐标。接下来,在第二部分,我们学习了如何从 OpenWeather API 中获取天气数据。本篇文章我们现在将为 WeatherView 编写 UI 代码,以便更友好地显示天气信息。

天气展示试图(WeatherView UI)

首先让我们编写 WeatherView 的主体。首先,我们需要使用 ZStack 。接下来我们将添加三个修饰符,一个用于覆盖底部安全区域,一个用于背景颜色,一个用于将 colorScheme 设置为深色。

ZStack(alignment: .leading) {

}
.edgesIgnoringSafeArea(.bottom)
.background(Color(hue: 0.656, saturation: 0.787, brightness: 0.354))
.preferredColorScheme(.dark)

让我们首先处理屏幕的上半部分。我们将在 ZStack 中添加一个 VStack,接着使用 padding 修饰符让周围留些空隙,最后使用 frame 修饰符填充整个屏幕的宽度。

// ./Views/WeatherView.swift

VStack {

}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)

在 VStack 内部,让我们为标题添加另一个 VStack。容器里用标题修饰符显示用户当前所在的城市,以及今天的日期。在它的正下方,添加一个 spacer 填充剩余的空间。

// ./Views/WeatherView.swift

VStack {
    VStack(alignment: .leading, spacing: 5) {
        Text(weather.name)
            .bold().font(.title)

        Text("Today, \(Date().formatted(.dateTime.month().day().hour().minute()))")
            .fontWeight(.light)
    }
    .frame(maxWidth: .infinity, alignment: .leading)

    Spacer()
}
.padding()
.frame(maxWidth: .infinity, alignment: .leading)

在 spacer 的正下方,我们将添加另一个 VStack 。这个容器里我们会引入天气相关的图标,同时在容器的最后通过 AsyncImage 的方式引入外部的图片(城市图标)。

// ./Views/WeatherView.swift

VStack {
    HStack {
        VStack(spacing: 20) {
            Image(systemName: "cloud")
                .font(.system(size: 40))

            Text("\(weather.weather[0].main)")
        }
        .frame(width: 150, alignment: .leading)

        Spacer()

        Text(weather.main.feelsLike.roundDouble() + "°")
            .font(.system(size: 100))
            .fontWeight(.bold)
            .padding()
    }

    Spacer()
        .frame(height:  80)

    AsyncImage(url: URL(string: "https://cdn.pixabay.com/photo/2020/01/24/21/33/city-4791269_960_720.png")) { image in
        image
            .resizable()
            .aspectRatio(contentMode: .fit)
            .frame(width: 350)
    } placeholder: {
        ProgressView()
    }

    Spacer()
}
.frame(maxWidth: .infinity)

接下来,我们将编写 UI 的底部部分,即底部的白色圆角矩形,让用户了解有关当前天气的更多信息。首先,我们需要在 Extensions 文件中添加另一个扩展方法,这样我们就可以很方便控制容器圆角的位置(左上,右上,右下,右左)等位置。

// ./Extensions.swift

import SwiftUI

extension View {
    func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View {
        clipShape(RoundedCorner(radius: radius, corners: corners) )
    }
}

struct RoundedCorner: Shape {
    var radius: CGFloat = .infinity
    var corners: UIRectCorner = .allCorners

    func path(in rect: CGRect) -> Path {
        let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius))
        return Path(path.cgPath)
    }
}

现在,让我们回到 WeatherView,我们将添加一个 VStack 。在其中,添加一个 Spacer 以将所有内容推到底部,并在其下方添加另一个 VStack。

// ./Views/WeatherView.swift

ZStack(alignment: .leading) {
        // First VStack for top half of screen here...

    VStack {
        Spacer()

                VStack {}
    }
}
.edgesIgnoringSafeArea(.bottom)
.background(Color(hue: 0.656, saturation: 0.787, brightness: 0.354))
.preferredColorScheme(.dark)

接下来我们在 ZStack 容器内应用子元素的排列方式,顶部对齐。然后在VStack 容器内定义顶部对齐,元素之间的距离为20。然后定义 VStack 容器的左上角和右上角为圆角。

// ./Views/WeatherView.swift

ZStack(alignment: .leading) {
        // First VStack for top half of screen here...

    VStack {
        Spacer()

        VStack(alignment: .leading, spacing: 20) {

        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding()
        .padding(.bottom, 20)
        .foregroundColor(Color(hue: 0.656, saturation: 0.787, brightness: 0.354))
        .background(.white)
        .cornerRadius(20, corners: [.topLeft, .topRight])
    }
}
.edgesIgnoringSafeArea(.bottom)
.background(Color(hue: 0.656, saturation: 0.787, brightness: 0.354))
.preferredColorScheme(.dark)

现在,我们新建个 Components 的文件夹,然后在文件夹内新建个 WeatherRow 的组件,方便我们在 WeatherView 文件里进行调用,这个组件有 图标(logo)、名称(name)、值(value)几个属性,分别用来显示天气对应图标,天气的属性及值。

// ./Components/WeatherRow.swift

struct WeatherRow: View {
    var logo: String
    var name: String
    var value: String

    var body: some View {
        HStack(spacing: 20) {
            Image(systemName: logo)
                .font(.title2)
                .frame(width: 20, height: 20)
                .padding()
                .background(Color(hue: 1.0, saturation: 0.0, brightness: 0.888))
                .cornerRadius(50)

            VStack(alignment: .leading, spacing: 8) {
                Text(name)
                    .font(.caption)

                Text(value)
                    .bold()
                    .font(.title)
            }
        }
    }
}

struct WeatherRow_Previews: PreviewProvider {
    static var previews: some View {
        WeatherRow(logo: "thermometer", name: "Feels like", value: "8°")
    }
}

接下来,我们回到 WeatherView 文件,将 WeatherRow 组件添加在其中,2个为1行,共4个,分别显示最低温度、最高温度、风速、湿度等,示例代码如下:

// ./Views/WeatherView.swift

VStack(alignment: .leading, spacing: 20) {
    Text("Weather now")
        .bold()
        .padding(.bottom)

    HStack {
        WeatherRow(logo: "thermometer", name: "Min temp", value: (weather.main.tempMin.roundDouble() + ("°")))
        Spacer()
        WeatherRow(logo: "thermometer", name: "Max temp", value: (weather.main.tempMax.roundDouble() + "°"))
    }

    HStack {
        WeatherRow(logo: "wind", name: "Wind speed", value: (weather.wind.speed.roundDouble() + " m/s"))
        Spacer()
        WeatherRow(logo: "humidity", name: "Humidity", value: "\(weather.main.humidity.roundDouble())%")
    }
}

最后,我们只需要在 ContentView 中调用 WeatherView 即可。在 if let weather = weather 语句中,让我们将 Text 替换为 WeatherView,将接口获取的天气信息传递给视图。

// ./Views/ContentView.swift

if let weather = weather {
        WeatherView(weather: weather)
}

测试应用

到这里我们的编码部分就完成了,你需要将应用安装到你的手机里去体验测试,运行后的效果如下图所示。

Final_weather_app.jpg
Final_weather_app.jpg

总结

到这里,我们的应用就从0到1的开发过程就完成了。从 Hello world 默认的初始化项目一直到这个漂亮的 UI 天气应用程序。我们学习了如何获取用户的当前位置,调用 API 来获取该位置的天气状况,并在漂亮的 UI 中显示所有信息。由于力求文章的简单容易理解,因此还有很多内容需要完善。以下是一些建议,您可以将应用程序优化的更好。

  • 添加动态的天气图标,而不是使用 SF symbol
  • 根据一天中的时间和天气条件添加动态背景颜色;
  • 根据当前位置添加当前城市特点的图片
  • 添加更多的城市,供用户查看,而不仅限于本地
  • 不要每次打卡应用,让用户授权获取地址信息
  • 由于我们使用的是免费服务,因此城市名称可能不准确。为了显示更准确的数据,我们可以在 Swift 中获取当前城市并将该名称传递给 OpenWeather API。
  • 以及更多。。。

版权声明

注:本文属于原创文章,版权属于「前端达人」公众号及 SwiftUI.cc 所有,谢绝一切形式的转载

更多精彩内容,请关注「前端达人」

欢迎关注「前端达人」
欢迎关注「前端达人」


Tags

#project
Previous Article
动手练一练,做一个简单的天气预报应用(weather app two)【中】
前端达人

前端达人

专注前端知识分享

Table Of Contents

1
天气展示试图(WeatherView UI)
2
测试应用
3
总结
4
版权声明

Related Posts

动手练一练,做一个简单的天气预报应用(weather app two)【中】
August 10, 2022
1 min

前端学习站

前端达人官网VUE官网React官网TypeScript官网

公众号:前端达人

京ICP备16033841号-8