当前位置:   article > 正文

助你打通SwiftUI任督二脉

swiftui

序言

开年的第一篇文章,今天分享的是SwiftUI,SwiftUI出来好几年,之前一直没学习,所以现在才开始;如果大家还留在 ​iOS​ 开发,这们语言也是一个趋势; 目前待业中.... 不得不说已逝的2023年,大家开始都抱着一解封,经济都会向上转好,可是现实不是我们想象那样;目前我也在学习 ​SwiftUI​,并且努力找工作中....;至于 ​2024​ 年经济如何,咱们作为老百姓在大环境和全球经济影响下;坦然面对,提升自己。 这里不得不说国人坚韧不拔的精神。“卷” -- 努力吧Coding人

SwiftUI体验

Xcode创建项目之后出现工程默认创建的UI界面;如下

一开始心里对自己说:

​"SwiftUI作为iOS开发新的UI体系,为啥初创的项目这么多代码,给初学者看到,一种压迫感,心想这语法好复杂,不想学了"​​;不管你是不是这样心里,我刚开始看见,这么一坨代码,没什么心思,于是索性删掉;按自己能理解学习的方式来操作;于是做了简化:

  1. import SwiftUI
  2. import SwiftData
  3. struct ContentView: View {
  4. var body: some View {
  5. Text("hello,word”)
  6. }
  7. }
  8. #Preview {
  9. ContentView()
  10. .modelContainer(for: Item.self, inMemory: true)
  11. }

关键字 some

关键字some啥玩意儿,完全陌生;先看看View;点击进入源码结构查看:

  1. @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
  2. public protocol View {
  3. /// The type of view representing the body of this view.
  4. ///
  5. /// When you create a custom view, Swift infers this type from your
  6. /// implementation of the required ``View/body-swift.property`` property.
  7. associatedtype Body : View
  8. @ViewBuilder @MainActor var body: Self.Body { get }
  9. }

一堆英文注解估计大家不喜欢看,我就没贴出来了;简单来说:
​View​​ 是一个泛型协议,它定义了所有视图类型需要遵循的接口,通过​​some​​修饰;​​表示​​ "我返回一个满足​​View​​ 协议的某种类型"。​​some​​关键字告诉 Swift,虽然我们知道​​body​​必须返回一个​​View​​,但我们不确定具体是哪种 ​​View​​(例如,​​Text​​, ​​Image​​, ​​VStack​​ 等)。

协议里有一个​​associatedtype​​,​​body​​,其实这种协议就是当作约束形式使用;只要遵守这种协议编译器每次闭包中返回的一定是一个确定,遵守​​View​​协议的类型。

那么苹果工程师利用Swift5.1 Opaque return types特性,开发者提供了一个灵活的开发模式,抹掉了具体的类型,不需要修改公共API来确定每次闭包的返回类型,也降低了代码书写难度。(学学苹果那些大神思想,不错)

在来看看Preview

  1. struct ContentView_Previews:PreviewProvider{
  2. static var previews: some View{
  3. ContentView()
  4. }
  5. }

​PreviewProvider​​就一个协议类,它的额作用提供swiftUI不用运行,就能直接看到UI渲染变化,我觉得这个挺好,减少开发人员对UI运行测试次数和时间,而​​previews​​就是一个静态属性,返回一个 ​​View​​ 对象,用于在预览面板中展示。

@State属性包装器

​@State属性包装器​​解决UI界面上,数据同步以及及时刷新的功能。一般来说数据更新完,界面 UI 同时更新。在 SwiftUI里面,视图中声明的任何状态、内容和布局,源头一旦发生改变,会自动更新视图,因此,只需要一次布局,这个时候出现了​​@State​​,它来解决与UI之间数据状态问题。

它的概念就是:​​@State​​ 是一个属性包装器(property wrapper),用于声明状态属性(state property)
当状态属性发生变化时,SwiftUI 会自动更新视图以反映最新的状态。

属性的值被存储在特殊的内存区域中,这个区域与 View struct 是隔离的 至于被它修饰的属性内存存储与分布现在无从得知,还没学习到那么深入,这事儿慢慢来,不是一天两天的,先上个代码看看它怎么使用的:

  1. import SwiftUI
  2. struct StateBootcamp: View {
  3. @State var bgkColor:Color = Color.blue
  4. @State var cut:Int = 0
  5. var body: some View {
  6. ZStack{
  7. bgkColor
  8. .ignoresSafeArea(.all)
  9. VStack(spacing: 20){
  10. Text("Hello, World!”)
  11. .font(.title)
  12. Text("count:\(cut)”)
  13. .font(.largeTitle)
  14. HStack(spacing: 20){
  15. Button("Button01") {
  16. cut+=1
  17. bgkColor = Color.red
  18. }
  19. .font(.title)
  20. .foregroundColor(.white)
  21. Button("Button02") {
  22. cut-=1
  23. bgkColor = .purple
  24. }
  25. .font(.title)
  26. .foregroundColor(.white)
  27. }
  28. Button("默认”){
  29. cut=0
  30. bgkColor = .blue
  31. }
  32. .font(.title)
  33. .foregroundColor(.white)
  34. }
  35. }
  36. }
  37. }
  38. #Preview {
  39. StateBootcamp()
  40. }

其实一看代码,就一幕了然,知道它的使用与作用;如果你写过swift代码,这些东西很好理解,但是只会OC,那么我建议你学习下swift;在来看swiftUI语法糖才更好理解。

在看看源码:

  1. @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
  2. @frozen @propertyWrapper public struct State<Value> : DynamicProperty {
  3. public init(wrappedValue value: Value)
  4. public init(initialValue value: Value)
  5. public var wrappedValue: Value { get nonmutating set }
  6. public var projectedValue: Binding<Value> { get }
  7. }
  8. @available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *)
  9. extension State where Value : ExpressibleByNilLiteral {
  10. /// Creates a state property without an initial value.
  11. ///
  12. /// This initializer behaves like the ``init(wrappedValue:)`` initializer
  13. /// with an input of `nil`. See that initializer for more information.
  14. @inlinable public init()
  15. }

可以看到​​State​​是一个结构体,由​​@propertyWrapper​​包装的。​​@propertyWrapper​​是属性包装器。property wrapper 做的事情大体如下:

  1. - 为底层的存储变量`State<Int>`自动提供一组 **getter****setter** 方法,结构体内保存了`Int`的具体数值;
  2. - 在 body 首次求值前,将`State<Int>`关联到当前`View`上,为它在堆中对应当前`View`分配一个存储位置。
  3. -`@State`修饰的变量设置观察,当值改变时,触发新一次的`body`求值,并刷新 UI。

SwiftUI基础组件

​Spacer垫片​:先贴贴代码

  1. import SwiftUI
  2. struct SpacerBootcampDemo: View {
  3. var body: some View {
  4. Text("Spacer UP”)
  5. .font(.largeTitle)
  6. Spacer()
  7. .frame(width: 37)
  8. .background(.blue)
  9. Text("Spacer Down”)
  10. .font(.largeTitle)
  11. }
  12. }
  13. #Preview {
  14. SpacerBootcampDemo()
  15. }

在看看效果图:

总结:Spacer 是一个灵活的空间视图,它的主要作用是在布局中自动调整自身的高度和宽度,以填满特定的空间;简单来说,它就是一个垫片,调整自身视图的高度,如果它周围有其他视图,也会受到Spacer影响。

​ScrollView​ 如果你之前使用UIkit框架开发,在用SwiftUI,一下有点不适应,代码和之前的 ​UIkit​ 开发模式不太一样,但是大大缩短UI编写时间;先上代码:

  1. import SwiftUI
  2. struct ScollViewBootcamp: View {
  3. var body: some View {
  4. ScrollView{
  5. LazyVStack{
  6. ForEach(0..<20){
  7. (idx) in
  8. VStack {
  9. Text("Hello, World!”)
  10. .font(.title)
  11. .foregroundStyle(.white)
  12. .frame(width: UIScreen.main.bounds.width-20,height: 350)
  13. .background(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<215)/255.0, green: CGFloat.random(in: 0..<235)/255.0, blue: CGFloat.random(in: 0...247)/255.0, alpha: 0.9)))
  14. .clipShape(RoundedRectangle(cornerRadius: 10))
  15. Rectangle()
  16. .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0...187)/255.0, green: CGFloat.random(in: 0..<210)/255.0, blue: CGFloat.random(in: 0...237)/255.0, alpha: 0.9)))
  17. .frame(width: UIScreen.main.bounds.width-20,height: 530)
  18. .clipShape(RoundedRectangle(cornerRadius: 10))
  19. ScrollView(.horizontal,showsIndicators: false,content: {
  20. LazyHStack{
  21. ForEach(0..<10){
  22. idx in
  23. Rectangle()
  24. .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0...167)/255.0, green: CGFloat.random(in: 0...131)/255.0, blue: CGFloat.random(in: 0...89)/255.0, alpha: 0.9)))
  25. .frame(width: 200, height: 300)
  26. .clipShape(RoundedRectangle(cornerRadius: 10))
  27. }
  28. }
  29. })
  30. .padding(.leading,10)
  31. .padding(.trailing,10)
  32. }
  33. }
  34. }
  35. .frame(width:UIScreen.main.bounds.width)
  36. }
  37. }
  38. }
  39. #Preview {
  40. ScollViewBootcamp()
  41. }

上图看看效果:

简单几句就能实现 ​ScrollView​ 的滑动效果;非常方便。

​LazyVGrid​ 网格布局,先上代码:

  1. import SwiftUI
  2. struct GridViewBootcamp: View {
  3. let columns=[
  4. GridItem(.flexible(),spacing: 6 ,alignment: .center),
  5. GridItem(.flexible(),spacing: 6 ,alignment: .center),
  6. GridItem(.flexible(),spacing: 6 ,alignment: .center),
  7. ]
  8. var body: some View {
  9. ScrollView{
  10. LazyVGrid(columns: columns,
  11. alignment: .center,
  12. spacing: 6,
  13. pinnedViews: [.sectionHeaders],content:
  14. {
  15. Section(content: {}, header: {
  16. Text("section header 一”)
  17. .font(.largeTitle)
  18. .foregroundStyle(.blue)
  19. .frame(width: UIScreen.main.bounds.width,height: 100,alignment: .leading)
  20. })
  21. ForEach(0..<41){
  22. index in
  23. Rectangle()
  24. .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<255)/255.0, green: CGFloat.random(in: 0..<255)/255.0, blue: CGFloat.random(in: 0...255)/255.0, alpha: 0.9)))
  25. .frame(height: 50)
  26. }
  27. //———
  28. Section {
  29. } header: {
  30. Text("section header 二”)
  31. .font(.largeTitle)
  32. .foregroundStyle(.blue)
  33. .frame(width: UIScreen.main.bounds.width,alignment: .leading)
  34. }
  35. ForEach(0..<41){
  36. index in
  37. Rectangle()
  38. .fill(Color.init(cgColor: CGColor(red: CGFloat.random(in: 0..<255)/255.0, green: CGFloat.random(in: 0..<255)/255.0, blue: CGFloat.random(in: 0...255)/255.0, alpha: 0.9)))
  39. .frame(height: 50)
  40. }
  41. })
  42. .padding(.leading,6)
  43. .padding(.trailing,6)
  44. .background(.gray)
  45. }.background(.blue)
  46. }
  47. }
  48. #Preview {
  49. GridViewBootcamp()
  50. }

效果图:

总结:LazyVGrid大家看到这个单词有个​​Lazy​​懒加载的意思,它的内部加载item简单来说,就是当视图需要时,才会执行item内容渲染功能,展示UI上。也就这点注意。

​SafeArea​安全区域:

  1. import SwiftUI
  2. struct SafeAreaBootcamp: View {
  3. var body: some View {
  4. GeometryReader{
  5. src in
  6. Rectangle()
  7. .fill(.blue)
  8. .frame(maxWidth: .infinity,
  9. maxHeight: .infinity)
  10. }
  11. }
  12. }
  13. #Preview {
  14. SafeAreaBootcamp()
  15. }

效果图:

可以看到上下边距存在安全区域的,如果禁用安全区域,使用 ​ignoresSafeArea(.all)​ 可以去掉。

代码如下:

最后说说SwiftUI函数表达

上上代码:

  1. import SwiftUI
  2. struct ExtractFunctionsBootcamp: View {
  3. @State var bgc:Color = .red
  4. var body: some View {
  5. normolView
  6. }
  7. var normolView : some View {
  8. setUI()
  9. }
  10. func chageColor() -> Void {
  11. self.bgc = .red
  12. }
  13. func setUI()->some View {
  14. return ZStack{
  15. bgc
  16. .ignoresSafeArea(.all)
  17. VStack(spacing: 20, content: {
  18. Text("Hello, World!”)
  19. .font(.largeTitle)
  20. Button(action: {
  21. bgc = .brown
  22. }, label: {
  23. Text(“Button”)
  24. .font(.largeTitle)
  25. .foregroundStyle(.white)
  26. })
  27. Button {
  28. self.chageColor()
  29. } label: {
  30. Image(systemName: “button.horizontal.top.press”)
  31. .resizable()
  32. .foregroundColor(.white)
  33. .aspectRatio(contentMode: .fill)
  34. .frame(width: 50,height: 50)
  35. }
  36. })
  37. }
  38. }
  39. }
  40. #Preview {
  41. ExtractFunctionsBootcamp()
  42. }

其实函数表达跟我们swift语法糖一样;​func​ 命名;这点和swift语法糖没什么区别。

总结(说说我的感想)

​优点:​

简洁性:Swift,SwiftUI语法简洁,编写代码变得更加容易和快速。

安全性:是一种类型安全的编程语言,可以在编译时检测类型错误,这帮助我们避免许多常见的错误,提高代码的质量和可靠性。

互操作性:它与Objective-C语言无缝互衔接,是的OC与swift代码混编变的更加便捷。

​说完优点在说缺点​

功能限制:虽然SwiftUI提供了许多常见的UI组件,但与UIKit相比,功能仍然相对有限。在某些复杂的界面需求下,可能需要使用UIKit来实现。

错误提示不明确:有时SwiftUI, SwiftUI的错误提示可能不够明确,导致难以定位问题。

UIkit与SwiftUI缺乏无缝兼容:两者兼容性不够理想,这在业务开发中,你可能才能发现。

目前苹果与市面大量应用也在使用Swift,SwiftUI开发应用,这们语言在应用中占有读也是成倍增长。

路漫漫其修远兮,吾将上下而求索

后续更新中……….

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/761897
推荐阅读
相关标签
  

闽ICP备14008679号