富海老翁 發表於 2021-5-6 19:22:00

IOS小组件(3):SwiftUI开发小组件布局入门

<h2 id="引言">引言</h2>
<p>  经过上一篇文章,我们已经可以在桌面上展示出一个小组件出来了,你肯定想小试牛刀,动手改一改,那我们就从改小组件的布局做起吧。本文不会讲解Swift语法,如果是熟悉Flutter,Kotlin这种语言的,问题也不大。本文只讲解小组件中常用的SwiftUI组件。</p>
<h2 id="本文大纲">本文大纲</h2>
<ul>
<li>小组件布局怎么区分组件型号:大中小</li>
<li>常用基础组件 Text Image</li>
<li>常用容器组件 ZStack VStack HStack</li>
<li>常用属性:充满父布局 文字内部居中 等分剩余空间(Spacer)</li>
</ul>
<h2 id="小组件布局怎么区分组件型号大中小">小组件布局怎么区分组件型号:大中小</h2>
<pre><code>struct Widget1EntryView : View {
&nbsp; &nbsp; // 这句代码能从上下文环境中取到小组件的型号
&nbsp; &nbsp; @Environment(\.widgetFamily) var family
&nbsp;&nbsp; &nbsp;
&nbsp; &nbsp; // 组件数据
&nbsp; &nbsp; var entry: Provider.Entry

&nbsp; &nbsp; // 这个 body 中就是自己需要实现的组件布局
&nbsp; &nbsp; var body: some View {
&nbsp; &nbsp; &nbsp; &nbsp; switch family {
&nbsp; &nbsp; &nbsp; &nbsp; case .systemSmall:&nbsp; // 小号
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Text(entry.date, style: .time)
&nbsp; &nbsp; &nbsp; &nbsp; case .systemMedium: // 中号
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Text(entry.date, style: .time)
&nbsp; &nbsp; &nbsp; &nbsp; case .systemLarge:&nbsp; // 大号
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Text(entry.date, style: .time)
&nbsp; &nbsp; &nbsp; &nbsp; @unknown default:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Text(entry.date, style: .time)
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; }
}
</code></pre>
<h2 id="常用基础组件text使用">常用基础组件Text使用</h2>
<pre><code>Text("普通文本")
&nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
// Text以日期作为参数时可以有以下多种使用方式,参考官网定义
// 重要:其中的.timer比较有用,可以用来做时钟的刷新
/// A predefined style used to display a `Date`.
public struct DateStyle {

&nbsp; &nbsp; /// A style displaying only the time component for a date.
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// &nbsp; &nbsp; Text(event.startDate, style: .time)
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// Example output:
&nbsp; &nbsp; /// &nbsp; &nbsp; 11:23PM
&nbsp; &nbsp; public static let time: Text.DateStyle

&nbsp; &nbsp; /// A style displaying a date.
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// &nbsp; &nbsp; Text(event.startDate, style: .date)
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// Example output:
&nbsp; &nbsp; /// &nbsp; &nbsp; June 3, 2019
&nbsp; &nbsp; public static let date: Text.DateStyle

&nbsp; &nbsp; /// A style displaying a date as relative to now.
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// &nbsp; &nbsp; Text(event.startDate, style: .relative)
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// Example output:
&nbsp; &nbsp; /// &nbsp; &nbsp; 2 hours, 23 minutes
&nbsp; &nbsp; /// &nbsp; &nbsp; 1 year, 1 month
&nbsp; &nbsp; public static let relative: Text.DateStyle

&nbsp; &nbsp; /// A style displaying a date as offset from now.
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// &nbsp; &nbsp; Text(event.startDate, style: .offset)
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// Example output:
&nbsp; &nbsp; /// &nbsp; &nbsp; +2 hours
&nbsp; &nbsp; /// &nbsp; &nbsp; -3 months
&nbsp; &nbsp; public static let offset: Text.DateStyle

&nbsp; &nbsp; /// A style displaying a date as timer counting from now.
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// &nbsp; &nbsp; Text(event.startDate, style: .timer)
&nbsp; &nbsp; ///
&nbsp; &nbsp; /// Example output:
&nbsp; &nbsp; ///&nbsp; &nbsp; 2:32
&nbsp; &nbsp; ///&nbsp; &nbsp; 36:59:01
&nbsp; &nbsp; public static let timer: Text.DateStyle
}
</code></pre>
<p>IOS中的颜色RGB不是安卓的0-255,而是0-1,这里写了一个拓展函数支持十六进制颜色字符串</p>
<pre><code>#if (arch(arm64) || arch(x86_64))

import Foundation
import SwiftUI

@available(iOS 13.0, *)
extension Color {
&nbsp;&nbsp; &nbsp;
&nbsp; &nbsp; //#ARGB
&nbsp; &nbsp; init?(hexString: String) {
&nbsp; &nbsp; &nbsp; &nbsp; var hex = hexString;
&nbsp; &nbsp; &nbsp; &nbsp; guard hexString.starts(with: "#") else {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return nil
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; hex.remove(at: hexString.startIndex)
&nbsp; &nbsp; &nbsp; &nbsp; var value: UInt64 = 0
&nbsp; &nbsp; &nbsp; &nbsp; Scanner(string: hex).scanHexInt64(&amp;value)

&nbsp; &nbsp; &nbsp; &nbsp; var a = 0xFF / 255.0
&nbsp; &nbsp; &nbsp; &nbsp; if hex.count &gt; 7 {
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; a = Double(value &gt;&gt; 24) / 255.0
&nbsp; &nbsp; &nbsp; &nbsp; }
&nbsp; &nbsp; &nbsp; &nbsp; let r = Double((value &amp; 0xFF0000) &gt;&gt; 16) / 255.0;
&nbsp; &nbsp; &nbsp; &nbsp; let g = Double((value &amp; 0xFF00) &gt;&gt; 8) / 255.0;
&nbsp; &nbsp; &nbsp; &nbsp; let b = Double(value &amp; 0xFF) / 255.0
&nbsp; &nbsp; &nbsp; &nbsp; self.init(red: Double(r), green: Double(g), blue: Double(b))
&nbsp; &nbsp; &nbsp; &nbsp; _ = self.opacity(Double(a))
&nbsp; &nbsp; }
}
</code></pre>
<h2 id="常用基础组件image使用">常用基础组件Image使用</h2>
<pre><code>// 访问bundle中的资源
Image("imageName")
// 通过UIImage加载文件夹中的图片资源
Image(uiImage: UIImage(contentsOfFile: "picPath") ?? UIImage())
&nbsp; &nbsp; .resizable()
&nbsp; &nbsp; .scaledToFill()
&nbsp; &nbsp; .clipped()
&nbsp; &nbsp; .colorMultiply(Color(hexString: config.textColor) ?? Color.white) // 重要:这个类似安卓中的colorFilter可以修改图片颜色
&nbsp; &nbsp; .frame(width: 36, height: 36, alignment: .center)
</code></pre>
<h2 id="常用容器组件zstack使用类似安卓里面的framelayout可以重叠布局">常用容器组件ZStack使用,类似安卓里面的FrameLayout,可以重叠布局</h2>
<pre><code>ZStack {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .time)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506191755120-905696836.png"></p>
<h2 id="常用容器组件hstack使用水平方向布局">常用容器组件HStack使用,水平方向布局</h2>
<pre><code>HStack {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .time)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506191812503-878772436.png"></p>
<h2 id="常用容器组件vstack使用垂直方向布局">常用容器组件VStack使用,垂直方向布局</h2>
<pre><code>VStack {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .time)
}.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506191822969-416967184.png"></p>
<h2 id="充满父布局怎么实现">充满父布局怎么实现</h2>
<pre><code>.frame(maxWidth: .infinity, maxHeight: .infinity)
VStack {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .time)
}
.frame(maxWidth: .infinity, maxHeight: .infinity)        // 充满父布局
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506192014663-1908205615.png"> <img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506192025638-1522461423.png"></p>
<h2 id="文字内部居中multilinetextalignment">文字内部居中(multilineTextAlignment)</h2>
<pre><code>.multilineTextAlignment(.center)
VStack {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .timer)
&nbsp; &nbsp; &nbsp; &nbsp; .multilineTextAlignment(.center)                // 让文字在Text内部居中
&nbsp; &nbsp; &nbsp; &nbsp; .background(Color(hexString: "#FFFF00"))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506192106141-1420085939.png"></p>
<h2 id="等分剩余空间spacer">等分剩余空间(Spacer)</h2>
<pre><code>VStack {
&nbsp; &nbsp; Spacer()
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Spacer()
&nbsp; &nbsp; Text(entry.date, style: .timer)
&nbsp; &nbsp; &nbsp; &nbsp; .multilineTextAlignment(.center)
&nbsp; &nbsp; &nbsp; &nbsp; .background(Color(hexString: "#FFFF00"))
&nbsp; &nbsp; Spacer()
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<p><img src="https://img2020.cnblogs.com/blog/823551/202105/823551-20210506192120180-1413267438.png"></p>
<h2 id="控制间距spacing">控制间距(spacing)</h2>
<pre><code>VStack(spacing: 10) {
&nbsp; &nbsp; Text("普通文本")
&nbsp; &nbsp; &nbsp; &nbsp; .font(.system(size: 15))&nbsp; &nbsp; // 字体
&nbsp; &nbsp; &nbsp; &nbsp; .foregroundColor(Color(hexString: "#FF0000"))
&nbsp; &nbsp; Text(entry.date, style: .timer)
&nbsp; &nbsp; &nbsp; &nbsp; .multilineTextAlignment(.center)
&nbsp; &nbsp; &nbsp; &nbsp; .background(Color(hexString: "#FFFF00"))
}
.frame(maxWidth: .infinity, maxHeight: .infinity)
.background(Color(hexString: "#00FFFF"))
</code></pre>
<h2 id="结语">结语</h2>
<p>  关于小组件SwiftUI布局就讲这么多,入个门差不多了,另外,小组件并不能使用全部的SwiftUI控件,只能使用一些基本的控件,更多详情可以查看官网 https://developer.apple.com/documentation/widgetkit/swiftui-views</p><br><br>
来源:https://www.cnblogs.com/popfisher/p/14736526.html
頁: [1]
查看完整版本: IOS小组件(3):SwiftUI开发小组件布局入门