SwiftUI框架详细解析 (二十五) —— 基于SwiftUI
版本记录
版本号 | 时间 |
---|---|
V1.0 | 2021.02.14 星期日 |
前言
今天翻阅苹果的API文档,发现多了一个框架SwiftUI,这里我们就一起来看一下这个框架。感兴趣的看下面几篇文章。
1. SwiftUI框架详细解析 (一) —— 基本概览(一)
2. SwiftUI框架详细解析 (二) —— 基于SwiftUI的闪屏页的创建(一)
3. SwiftUI框架详细解析 (三) —— 基于SwiftUI的闪屏页的创建(二)
4. SwiftUI框架详细解析 (四) —— 使用SwiftUI进行苹果登录(一)
5. SwiftUI框架详细解析 (五) —— 使用SwiftUI进行苹果登录(二)
6. SwiftUI框架详细解析 (六) —— 基于SwiftUI的导航的实现(一)
7. SwiftUI框架详细解析 (七) —— 基于SwiftUI的导航的实现(二)
8. SwiftUI框架详细解析 (八) —— 基于SwiftUI的动画的实现(一)
9. SwiftUI框架详细解析 (九) —— 基于SwiftUI的动画的实现(二)
10. SwiftUI框架详细解析 (十) —— 基于SwiftUI构建各种自定义图表(一)
11. SwiftUI框架详细解析 (十一) —— 基于SwiftUI构建各种自定义图表(二)
12. SwiftUI框架详细解析 (十二) —— 基于SwiftUI创建Mind-Map UI(一)
13. SwiftUI框架详细解析 (十三) —— 基于SwiftUI创建Mind-Map UI(二)
14. SwiftUI框架详细解析 (十四) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(一)
15. SwiftUI框架详细解析 (十五) —— 基于Firebase Cloud Firestore的SwiftUI iOS程序的持久性添加(二)
16. SwiftUI框架详细解析 (十六) —— 基于SwiftUI简单App的Dependency Injection应用(一)
17. SwiftUI框架详细解析 (十七) —— 基于SwiftUI简单App的Dependency Injection应用(二)
18. SwiftUI框架详细解析 (十八) —— Firebase Remote Config教程(一)
19. SwiftUI框架详细解析 (十九) —— Firebase Remote Config教程(二)
20. SwiftUI框架详细解析 (二十) —— 基于SwiftUI的Document-Based App的创建(一)
21. SwiftUI框架详细解析 (二十一) —— 基于SwiftUI的Document-Based App的创建(二)
22. SwiftUI框架详细解析 (二十二) —— 基于SwiftUI的AWS AppSync框架的使用(一)
23. SwiftUI框架详细解析 (二十三) —— 基于SwiftUI的AWS AppSync框架的使用(二)
24. SwiftUI框架详细解析 (二十四) —— 基于SwiftUI的编辑占位符的使用(一)
源码
1. Swift
下面就是源码了
1. AppMain.swift
import SwiftUI
@main
struct AppMain: App {
var body: some Scene {
WindowGroup {
QuotesView()
}
}
}
2. QuotesView.swift
import SwiftUI
struct QuotesView: View {
@ObservedObject var viewModel = QuotesViewModel()
var body: some View {
ZStack {
NavigationView {
List {
ForEach(viewModel.quotes) { quote in
row(from: quote)
}
.redacted(
reason: viewModel.shouldHideContent ? .placeholder : []
)
}
.navigationTitle("Quotation")
}
if viewModel.quotes.isEmpty {
ProgressView()
}
}
}
private func row(from quote: Quote) -> some View {
HStack(spacing: 12) {
Image(systemName: quote.iconName)
.resizable()
.aspectRatio(nil, contentMode: .fit)
.frame(width: 20)
.unredacted()
VStack(alignment: .leading) {
Text(quote.content)
.font(
.system(
size: 17,
weight: .medium,
design: .rounded
)
)
Text(quote.createdDate, style: .date)
.font(
.system(
size: 15,
weight: .bold,
design: .rounded
)
)
.foregroundColor(.secondary)
}
}
}
}
3. QuotesViewModel.swift
import SwiftUI
final class QuotesViewModel: ObservableObject {
@Published var isLoading = false
@Published var quotes: [Quote] = []
@Published var shouldConceal = false
var shouldHideContent: Bool {
return shouldConceal || isLoading
}
init() {
beginObserving()
isLoading = true
let simulatedRequestDelay = Double.random(in: 1..<3)
delay(interval: simulatedRequestDelay) {
withAnimation {
self.quotes = ModelLoader.bundledQuotes
}
let simulatedIngestionDelay = Double.random(in: 1..<3)
self.delay(interval: simulatedIngestionDelay) {
self.isLoading = false
}
}
}
private func delay(interval: TimeInterval, block: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + interval) {
block()
}
}
private func beginObserving() {
let center = NotificationCenter.default
center.addObserver(
self,
selector: #selector(appMovedToBackground),
name: UIApplication.willResignActiveNotification,
object: nil
)
center.addObserver(
self,
selector: #selector(appMovedToForeground),
name: UIApplication.didBecomeActiveNotification,
object: nil
)
}
@objc private func appMovedToForeground() {
shouldConceal = false
}
@objc private func appMovedToBackground() {
shouldConceal = true
}
}
4. ModelLoader.swift
import Foundation
enum ModelLoader {
static var bundledQuotes: [Quote] {
guard let url = Bundle.main.url(forResource: "quotes", withExtension: "json") else {
return []
}
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .secondsSince1970
do {
let data = try Data(contentsOf: url)
return try decoder.decode([Quote].self, from: data)
} catch {
print(error)
return []
}
}
}
5. Quote.swift
import Foundation
struct Quote: Decodable, Identifiable, Equatable {
let id: String
let content: String
let createdDate: Date
let iconName: String
init() {
id = "default"
content = "Focus on your goal. Don't look in any direction but ahead."
createdDate = Date()
iconName = "sun.min.fill"
}
}
6. QuoteOfTheHour.swift
import WidgetKit
import SwiftUI
struct Provider: TimelineProvider {
func placeholder(in context: Context) -> QuoteEntry {
QuoteEntry(model: Quote(), date: Date())
}
func getSnapshot(in context: Context, completion: @escaping (QuoteEntry) -> Void) {
let entry = QuoteEntry(model: Quote(), date: Date())
completion(entry)
}
func getTimeline(in context: Context, completion: @escaping (Timeline<Entry>) -> Void) {
var entries: [QuoteEntry] = []
var quotes = ModelLoader.bundledQuotes
let calendar = Calendar.current
let currentDate = Date()
for hourOffset in 0..<24 {
guard let entryDate = calendar.date(byAdding: .hour, value: hourOffset, to: currentDate) else {
continue
}
guard let randomQuote = quotes.randomElement() else {
continue
}
if let index = quotes.firstIndex(of: randomQuote) {
quotes.remove(at: index)
}
entries.append(QuoteEntry(model: randomQuote, date: entryDate))
}
let timeline = Timeline(entries: entries, policy: .atEnd)
completion(timeline)
}
}
struct QuoteEntry: TimelineEntry {
let model: Quote
let date: Date
}
struct QuoteOfTheHourEntryView: View {
var entry: Provider.Entry
var body: some View {
VStack(alignment: .leading) {
HStack {
Image(systemName: entry.model.iconName)
.resizable()
.aspectRatio(nil, contentMode: .fit)
.frame(width: 12)
.unredacted()
Spacer()
Text(entry.model.createdDate, style: .date)
.font(
.system(
size: 12,
weight: .bold,
design: .rounded
)
)
.foregroundColor(.secondary)
.multilineTextAlignment(.trailing)
}
Text(entry.model.content)
.font(
.system(
size: 16,
weight: .medium,
design: .rounded
)
)
Spacer()
}
.padding(12)
}
}
@main
struct QuoteOfTheHour: Widget {
let kind: String = "QuoteOfTheHour"
var body: some WidgetConfiguration {
StaticConfiguration(kind: kind, provider: Provider()) { entry in
QuoteOfTheHourEntryView(entry: entry)
}
.supportedFamilies([.systemSmall, .systemMedium])
.configurationDisplayName("Quote Of The Hour")
.description("Your hourly inspiration.")
}
}
后记
本篇主要讲述了基于SwiftUI的编辑占位符的使用,感兴趣的给个赞或者关注~~~