Преглед изворни кода

登录页面ui实现

Toast实现
各种依赖宝导入
main
jasonsyf пре 1 година
родитељ
комит
d5afb201de

+ 1
- 1
iOSFirst.xcodeproj/xcuserdata/sunyufeng.xcuserdatad/xcschemes/xcschememanagement.plist Прегледај датотеку

@@ -7,7 +7,7 @@
7 7
 		<key>iOSFirst.xcscheme_^#shared#^_</key>
8 8
 		<dict>
9 9
 			<key>orderHint</key>
10
-			<integer>0</integer>
10
+			<integer>17</integer>
11 11
 		</dict>
12 12
 	</dict>
13 13
 </dict>

+ 23
- 0
iOSFirst/Assets.xcassets/txtlogo.imageset/Contents.json Прегледај датотеку

@@ -0,0 +1,23 @@
1
+{
2
+  "images" : [
3
+    {
4
+      "filename" : "txtlogo.png",
5
+      "idiom" : "universal",
6
+      "scale" : "1x"
7
+    },
8
+    {
9
+      "filename" : "txtlogo@2x.png",
10
+      "idiom" : "universal",
11
+      "scale" : "2x"
12
+    },
13
+    {
14
+      "filename" : "txtlogo@3x.png",
15
+      "idiom" : "universal",
16
+      "scale" : "3x"
17
+    }
18
+  ],
19
+  "info" : {
20
+    "author" : "xcode",
21
+    "version" : 1
22
+  }
23
+}

BIN
iOSFirst/Assets.xcassets/txtlogo.imageset/txtlogo.png Прегледај датотеку


BIN
iOSFirst/Assets.xcassets/txtlogo.imageset/txtlogo@2x.png Прегледај датотеку


BIN
iOSFirst/Assets.xcassets/txtlogo.imageset/txtlogo@3x.png Прегледај датотеку


+ 127
- 12
iOSFirst/ContentView.swift Прегледај датотеку

@@ -9,18 +9,133 @@ import SwiftUI
9 9
 
10 10
 struct ContentView: View {
11 11
     var body: some View {
12
-        VStack {
13
-            Image(systemName: "globe")
14
-                .imageScale(.large)
15
-                .foregroundColor(.accentColor)
16
-            Text("Hello, world!")
17
-        }
18
-        .padding()
12
+        VStack(alignment:.leading,spacing: 0) {
13
+            HStack{
14
+                Spacer()
15
+                Image(systemName: "globe")
16
+                    .imageScale(.large)
17
+                    .foregroundColor(.accentColor)
18
+                Text("设置").font(.system(size: 14))
19
+            }
20
+            
21
+            Text("欢迎来到")
22
+                .foregroundColor(.gray)
23
+                .font(.system(size: 30))
24
+                .multilineTextAlignment(.center)
25
+                .bold()
26
+                .padding(.top,20)
27
+            
28
+            
29
+            HStack{
30
+                Text("logo")
31
+                    .foregroundColor(.gray)
32
+                    .font(.system(size: 30))
33
+                    .multilineTextAlignment(.center)
34
+                    .bold()
35
+                Image("txtlogo")
36
+                    .imageScale(.large)
37
+                    .foregroundColor(.accentColor)
38
+                    .frame(width: 240,height: 35)
39
+            }
40
+            .padding(.top,20)
41
+            .padding(.bottom,100)
42
+            
43
+            VStack(alignment: .leading, spacing: 400) {
44
+                HStack(alignment: .center, spacing: 15) {
45
+                    Image(systemName: "globe")
46
+                        .imageScale(.large)
47
+                        .foregroundColor(.accentColor)
48
+                    
49
+                    TextField("请输入手机号",text:.constant(""))
50
+                        .font(.system(size: 16))
51
+                        .frame(maxWidth: .infinity,alignment: .center)
52
+                    
53
+                }
54
+                
55
+            }
56
+            .frame(height: 80)
57
+            
58
+            Divider()
59
+            VStack(alignment: .leading, spacing: 400) {
60
+                HStack(alignment: .center, spacing: 15) {
61
+                    Image(systemName: "globe")
62
+                        .imageScale(.large)
63
+                        .foregroundColor(.accentColor)
64
+                    
65
+                    TextField("请输入密码",text:.constant(""))
66
+                        .font(.system(size: 16))
67
+                        .frame(maxWidth: .infinity,alignment: .center)
68
+                    
69
+                    
70
+                }
71
+                
72
+            }.frame(height: 80)
73
+            Divider().padding(.bottom,40)
74
+            
75
+            
76
+            VStack(alignment: .trailing, spacing: 400) {
77
+                
78
+                VStack(alignment: .center, spacing: 15) {
79
+                    Button(action: {
80
+                        let msg="点击了登录按钮"
81
+                        print(msg)
82
+                        SUIToast.show(messageItem: .init(
83
+                                            message: msg,
84
+                                            bgColor: .gray,
85
+                                            messageColor: .white
86
+                                        ))
87
+                    }) {
88
+                        Text("登录")
89
+                            .font(.system(size: 17))
90
+                            .bold()
91
+                            .frame(minWidth: 0, maxWidth: .infinity)
92
+                            .padding()
93
+                            .foregroundColor(.white)
94
+                            .background(.blue)
95
+                            .cornerRadius(30)
96
+                    }
97
+                    
98
+                }
99
+                	
100
+            }
101
+            
102
+            HStack{
103
+                Text("验证码登录").font(.system(size:15)).onTapGesture {
104
+                    let msg="点击了验证码登录"
105
+                    print(msg)
106
+                    SUIToast.show(messageItem: .init(
107
+                                        message: msg,
108
+                                        bgColor: .gray,
109
+                                        messageColor: .white
110
+                                    ))
111
+                }
112
+                Spacer ()
113
+                Text("忘记密码?").font(.system(size:15)).onTapGesture {
114
+                    let msg="点击了密码"
115
+                    print(msg)
116
+                    SUIToast.show(messageItem: .init(
117
+                                        message: msg,
118
+                                        bgColor: .gray,
119
+                                        messageColor: .white
120
+                                    ))
121
+                    
122
+                    
123
+                }.padding(.vertical,20).font(.system(size: 15))
124
+                
125
+                
126
+            }
127
+            Spacer()
128
+        
129
+        }.padding()
130
+        .frame(width: .infinity,height: .infinity)
19 131
     }
132
+    
20 133
 }
21
-
22
-struct ContentView_Previews: PreviewProvider {
23
-    static var previews: some View {
24
-        ContentView()
134
+    
135
+    struct ContentView_Previews: PreviewProvider {
136
+        static var previews: some View {
137
+            ContentView()
138
+        }
25 139
     }
26
-}
140
+    
141
+

+ 48
- 0
iOSFirst/Launch Screen.storyboard Прегледај датотеку

@@ -0,0 +1,48 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21507" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3
+    <device id="retina6_12" orientation="portrait" appearance="light"/>
4
+    <dependencies>
5
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21505"/>
6
+        <capability name="Safe area layout guides" minToolsVersion="9.0"/>
7
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
8
+    </dependencies>
9
+    <scenes>
10
+        <!--View Controller-->
11
+        <scene sceneID="EHf-IW-A2E">
12
+            <objects>
13
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
14
+                    <view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
15
+                        <rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
16
+                        <autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
17
+                        <subviews>
18
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" minimumFontSize="9" translatesAutoresizingMaskIntoConstraints="NO" id="obG-Y5-kRd" userLabel="就是玩">
19
+                                <rect key="frame" x="0.0" y="832" width="393" height="0.0"/>
20
+                                <fontDescription key="fontDescription" type="system" pointSize="17"/>
21
+                                <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
22
+                                <nil key="highlightedColor"/>
23
+                            </label>
24
+                            <label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="哈哈哈这是启动页" textAlignment="center" lineBreakMode="middleTruncation" baselineAdjustment="alignBaselines" minimumFontSize="18" translatesAutoresizingMaskIntoConstraints="NO" id="GJd-Yh-RWb">
25
+                                <rect key="frame" x="0.0" y="263.66666666666669" width="393" height="43"/>
26
+                                <fontDescription key="fontDescription" type="boldSystem" pointSize="36"/>
27
+                                <color key="textColor" red="0.0" green="0.0" blue="0.0" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
28
+                                <nil key="highlightedColor"/>
29
+                            </label>
30
+                        </subviews>
31
+                        <viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
32
+                        <color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
33
+                        <constraints>
34
+                            <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="obG-Y5-kRd" secondAttribute="centerX" id="5cz-MP-9tL"/>
35
+                            <constraint firstItem="Bcu-3y-fUS" firstAttribute="centerX" secondItem="GJd-Yh-RWb" secondAttribute="centerX" id="Q3B-4B-g5h"/>
36
+                            <constraint firstItem="obG-Y5-kRd" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="SfN-ll-jLj"/>
37
+                            <constraint firstAttribute="bottom" secondItem="obG-Y5-kRd" secondAttribute="bottom" constant="20" id="Y44-ml-fuU"/>
38
+                            <constraint firstItem="GJd-Yh-RWb" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="bottom" multiplier="1/3" constant="1" id="moa-c2-u7t"/>
39
+                            <constraint firstItem="GJd-Yh-RWb" firstAttribute="leading" secondItem="Bcu-3y-fUS" secondAttribute="leading" symbolic="YES" id="x7j-FC-K8j"/>
40
+                        </constraints>
41
+                    </view>
42
+                </viewController>
43
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
44
+            </objects>
45
+            <point key="canvasLocation" x="53" y="375"/>
46
+        </scene>
47
+    </scenes>
48
+</document>

+ 21
- 0
iOSFirst/Preview Content/Preview Assets.xcassets/simulator_screenshot_1E921C5D-2C8A-4A9D-B20A-A5C48CD914E2.imageset/Contents.json Прегледај датотеку

@@ -0,0 +1,21 @@
1
+{
2
+  "images" : [
3
+    {
4
+      "idiom" : "universal",
5
+      "scale" : "1x"
6
+    },
7
+    {
8
+      "filename" : "simulator_screenshot_1E921C5D-2C8A-4A9D-B20A-A5C48CD914E2.png",
9
+      "idiom" : "universal",
10
+      "scale" : "2x"
11
+    },
12
+    {
13
+      "idiom" : "universal",
14
+      "scale" : "3x"
15
+    }
16
+  ],
17
+  "info" : {
18
+    "author" : "xcode",
19
+    "version" : 1
20
+  }
21
+}

BIN
iOSFirst/Preview Content/Preview Assets.xcassets/simulator_screenshot_1E921C5D-2C8A-4A9D-B20A-A5C48CD914E2.imageset/simulator_screenshot_1E921C5D-2C8A-4A9D-B20A-A5C48CD914E2.png Прегледај датотеку


+ 21
- 0
iOSFirst/Preview Content/Preview Assets.xcassets/截屏2022-12-20 23.22.36.imageset/Contents.json Прегледај датотеку

@@ -0,0 +1,21 @@
1
+{
2
+  "images" : [
3
+    {
4
+      "idiom" : "universal",
5
+      "scale" : "1x"
6
+    },
7
+    {
8
+      "filename" : "截屏2022-12-20 23.22.36.png",
9
+      "idiom" : "universal",
10
+      "scale" : "2x"
11
+    },
12
+    {
13
+      "idiom" : "universal",
14
+      "scale" : "3x"
15
+    }
16
+  ],
17
+  "info" : {
18
+    "author" : "xcode",
19
+    "version" : 1
20
+  }
21
+}

BIN
iOSFirst/Preview Content/Preview Assets.xcassets/截屏2022-12-20 23.22.36.imageset/截屏2022-12-20 23.22.36.png Прегледај датотеку


+ 259
- 0
iOSFirst/SwiftUIToast.swift Прегледај датотеку

@@ -0,0 +1,259 @@
1
+//
2
+//  SwiftUIToast.swift
3
+//  iOSFirst
4
+//
5
+//  Created by 孙宇峰 on 2023/2/2.
6
+//
7
+
8
+
9
+import SwiftUI
10
+
11
+
12
+/**
13
+ SUIToast
14
+ 
15
+ Alias of SUIToastController
16
+ 
17
+ */
18
+public var SUIToast : SUIToastController {
19
+    return SUIToastController.shared
20
+}
21
+
22
+
23
+
24
+/**
25
+ SUIToastController
26
+ 
27
+ Supposedly a singleton to wrap and control all toast items
28
+ 
29
+ */
30
+public class SUIToastController : ObservableObject {
31
+    
32
+    public static var shared = SUIToastController()
33
+    
34
+    public var toastLengthLong = 3.5
35
+    
36
+    public var toastLengthShort = 2.0
37
+
38
+    @Published var items: [SUIToastViewCellItem] = []
39
+    
40
+    private var updateTimer: Timer?
41
+    
42
+    func initialize() {
43
+        startTimer()
44
+    }
45
+    
46
+    func uninitialize() {
47
+        stopTimer()
48
+    }
49
+    
50
+    public func show(_ message: String) {
51
+        items.append(
52
+            .init(
53
+                message: message
54
+            )
55
+        )
56
+    }
57
+    
58
+    public func show(messageItem: SUIToastViewCellItem) {
59
+        items.append(
60
+            messageItem
61
+        )
62
+    }
63
+    
64
+    func startTimer() {
65
+        updateTimer = .scheduledTimer(
66
+            timeInterval: 0.1,
67
+            target: self,
68
+            selector: #selector(SUIToastController.onUpdate),
69
+            userInfo: nil,
70
+            repeats: true)
71
+    }
72
+    
73
+    func stopTimer() {
74
+        if let updateTimer = updateTimer {
75
+            updateTimer.invalidate()
76
+            self.updateTimer = nil
77
+        }
78
+    }
79
+    
80
+    @objc func onUpdate() {
81
+        var isUpdated = false
82
+        
83
+        for k in (0 ..< items.count).reversed() {
84
+            let it = items[k]
85
+            if it.isExpired {
86
+                items.remove(at: k)
87
+                isUpdated = true
88
+            }
89
+        }
90
+        
91
+        if isUpdated {
92
+            self.objectWillChange.send()
93
+        }
94
+    }
95
+}
96
+
97
+public struct SUIToastViewCellItem: Identifiable, Hashable {
98
+    
99
+    public var id: String
100
+    public var message: String
101
+    public var bgColor: Color
102
+    public var messageColor: Color
103
+    public var createdAt: Date
104
+    public var toastLength: CGFloat
105
+    
106
+    public init(
107
+        message: String = "",
108
+        length: CGFloat = SUIToast.toastLengthLong,
109
+        bgColor: Color = .black,
110
+        messageColor: Color = .white
111
+    ) {
112
+        self.id = UUID().uuidString
113
+        self.message = message
114
+        self.bgColor = bgColor
115
+        self.messageColor = messageColor
116
+        self.createdAt = Date.now
117
+        self.toastLength = length
118
+    }
119
+
120
+    var isExpired: Bool {
121
+        get {
122
+            return Date.now.timeIntervalSinceReferenceDate - createdAt.timeIntervalSinceReferenceDate > toastLength
123
+        }
124
+    }
125
+}
126
+
127
+
128
+
129
+
130
+public struct SUIToastViewContainer: View {
131
+    
132
+    @StateObject var toastObs = SUIToastController.shared
133
+    
134
+    public enum StackAlignment {
135
+        case top
136
+        case bottom
137
+        case middle
138
+    }
139
+    
140
+    public enum StackOverlap {
141
+        case overlap
142
+        case stack
143
+    }
144
+    
145
+    var stackAlignment: StackAlignment = .bottom
146
+    var stackOverlap: StackOverlap = .overlap
147
+    
148
+    public init() {
149
+        
150
+    }
151
+    
152
+    
153
+    public init(
154
+        stackAlignment: StackAlignment = .bottom,
155
+        stackOverlap: StackOverlap = .overlap,
156
+        toastObs: StateObject<SUIToastController>? = nil
157
+    ) {
158
+        self.stackAlignment = stackAlignment
159
+        self.stackOverlap = stackOverlap
160
+        
161
+        if let toastObs = toastObs {
162
+            self._toastObs = toastObs
163
+        }
164
+    }
165
+    
166
+    
167
+    
168
+    public var body: some View {
169
+        ZStack {
170
+            VStack(spacing: 0) {
171
+                if stackAlignment == .bottom
172
+                    || stackAlignment == .middle {
173
+                    Spacer()
174
+                }
175
+                
176
+                if stackOverlap == .overlap {
177
+                    ZStack(alignment: .center) {
178
+                        ForEach(
179
+                            toastObs.items,
180
+                            id: \.id
181
+                        )
182
+                        { it in
183
+                            
184
+                            VStack(spacing: 0) {
185
+                                Spacer()
186
+                                SUIToastViewCell(
187
+                                    item: it
188
+                                )
189
+                                .zIndex(it.createdAt.timeIntervalSinceReferenceDate)
190
+                                .transition(
191
+                                    .opacity.combined(with: .move(edge: .bottom))
192
+                                )
193
+                            }
194
+                            
195
+                            
196
+                        }
197
+                    }
198
+
199
+                }
200
+                else if stackOverlap == .stack
201
+                {
202
+                    ForEach(
203
+                        toastObs.items.reversed(),
204
+                        id: \.id
205
+                    )
206
+                    { it in
207
+                        
208
+                        SUIToastViewCell(
209
+                            item: it
210
+                        )
211
+                        .transition(
212
+                            .opacity.combined(with: .move(edge: .bottom))
213
+                        )
214
+                        
215
+                    }
216
+
217
+                }
218
+                
219
+                
220
+
221
+                if stackAlignment == .top
222
+                    || stackAlignment == .middle {
223
+                    Spacer()
224
+                }
225
+            }
226
+        }
227
+        .animation(.default, value: toastObs.items)
228
+        .onAppear {
229
+            toastObs.initialize()
230
+        }
231
+        .onDisappear {
232
+            toastObs.uninitialize()
233
+        }
234
+    }
235
+}
236
+
237
+internal struct SUIToastViewCell: View {
238
+    var item: SUIToastViewCellItem
239
+    
240
+    var body: some View {
241
+        ZStack {
242
+            VStack(spacing: 0) {
243
+                HStack(spacing: 8) {
244
+                    Text(item.message)
245
+                        .foregroundColor(item.messageColor)
246
+                }
247
+            }
248
+            .padding(.horizontal, 20)
249
+            .padding(.vertical, 10)
250
+            .background(
251
+                RoundedRectangle(cornerRadius: 10)
252
+                    .fill(item.bgColor)
253
+            )
254
+        }
255
+        .padding(.horizontal, 20)
256
+        .padding(.vertical, 10)
257
+        .allowsHitTesting(false)
258
+    }
259
+}

+ 2
- 0
iOSFirst/iOSFirstApp.swift Прегледај датотеку

@@ -9,9 +9,11 @@ import SwiftUI
9 9
 
10 10
 @main
11 11
 struct iOSFirstApp: App {
12
+    
12 13
     var body: some Scene {
13 14
         WindowGroup {
14 15
             ContentView()
16
+            SUIToastViewContainer(stackOverlap: .overlap)
15 17
         }
16 18
     }
17 19
 }

Loading…
Откажи
Сачувај