swiftui构建列表和导航(这些Swift实用性技巧的操作说明)
swiftui构建列表和导航(这些Swift实用性技巧的操作说明)// 1 - Ugly Code var strings = ["Hello" "This" "Is" "A" "Test"] var integers = [1 2 3 4 5 6 7] func printStrings(_ array: [String]) { for s in array { print(s) } } func printIntegers(_ array: [Int]) { for i in array { print(i) } } // 1 - In Action printStrings(strings) printIntegers(integers) // 2 - Clean Code func printArray<T>(_ array: [T]) { for item in array { print(e
全文共8828字,预计学习时长16分钟本文介绍的这些Swift实用性技巧的操作说明可以用于创建iOS应用程序,希望你可从中有所收获。
1. 可选项解包
var optional1: Int? var optional2: Int? // 1 - Ugly Code func unwrap() { if let o1 = optional1 { if let o2 = optional2 { // reached } } } // 2 - Clean Code func unwrap() { guard let o1 = optional1 let o2 = optional2 else { return } // reached }
解包可选变量,在一行中使用选项2处理故障。
如果两个可选值都不是nil值,则选项2会被注释。如果其中一个变量为nil值,则“返回”并退出此函数的范围。
2. 三元运算符
let myBool = true let testBool = myBool ? false : true let testInt = myBool ? 16 : 8 //testBool equals false testInt equals 16 because the input to the conditional is true therefore //the first choice is picked for each operator //ternary let ternary = conditional ? true : false //vs. conditional var ternary = false if conditional { ternary = true }
通过这些有用的运算符,将条件逻辑压缩到一行。根据状态设置变量值。
是不是更方便?如果读起来有点难,那么可将这些运算符嵌套为紧凑的代码。
//double stack let ternaryDouble = conditional ? true : (conditional2 ? true : false) //triple stack let ternaryTriple = conditional ? true : (conditional2 ? true : (conditional3 ? true : false))
3. 泛型
// 1 - Ugly Code var strings = ["Hello" "This" "Is" "A" "Test"] var integers = [1 2 3 4 5 6 7] func printStrings(_ array: [String]) { for s in array { print(s) } } func printIntegers(_ array: [Int]) { for i in array { print(i) } } // 1 - In Action printStrings(strings) printIntegers(integers) // 2 - Clean Code func printArray<T>(_ array: [T]) { for item in array { print(element) } } // 2 - In Action printArray(strings) printArray(integers)
根据谷歌定义,泛型编程是一种编写函数和数据类型的方法,同时对所使用的数据类型做出最小化假设。
考虑到抽象化,使用泛型可生成更为清晰的代码,减少错误。
如上文所示,通过选择选项2,可编写一个函数(相对于多个),处理几种不同类型的输入。
4. 通过十六进制代码生成UIColor
创建一个名为 UIColor Extensions.swift的文件,包含以下代码:
import UIKit extension UIColor { convenience init(hex:Int alpha: CGFloat = 1.0) { self.init( red: CGFloat((hex & 0xFF0000) >> 16) / 255.0 green: CGFloat((hex & 0x00FF00) >> 8) / 255.0 blue: CGFloat((hex & 0x0000FF) >> 0) / 255.0 alpha: alpha ) } }
恭喜,现在你可通过十六进制代码生成不同颜色,如下所示:
let green = UIColor(hex: 0x1faf46)
let red = UIColor(hex: 0xfe5960)
let blue = UIColor(hex: 0x0079d5)
5. 使用扩展程序
import UIKit extension UIButton { func press(completion: @escaping() -> ()) { UIView.animate(withDuration: 0.125 delay: 0 options: [.curveEaseIn] animations: { self.transform = CGAffineTransform(scaleX: 0.94 y: 0.94) } completion: { _ in UIView.animate(withDuration: 0.125 delay: 0 options: [.curveEaseIn] animations: { self.transform = .identity } completion: { _ in completion() }) }) } }
创建一个可扩展文件,包含经常重复使用的类。
在这种情况下,笔者选择了UIButton演示添加自定义印刷功能。
现在,可在添加UIButton的地方,调用press函数模拟动画进行放大和缩小,如下所示:
let myButton = UIButton()
myButton.press() {
//handle completion
}
6. 通过创建一个类汇集多种后端/函数调用
想象一下,你需要在应用程序内部调用一个函数来更新本地数据,如下所示:
FAB.updateData()
在该例中,FAB代表Google Firebase。现在,设想要清除Firebase,用另一个后端进行替换,这一技巧将使这种情况变得快速又简单。
编写代码时,如果发现自己在应用程序中多次调用相同函数,那么请创建一个类,将所调用函数“汇集”到一个函数中,然后调用你的网络代码。
例如:
// 1 - Bad Code class MyClass1 { init() { FAB.updateData() } } class MyClass2 { init() { FAB.updateData() } } class MyClass3 { init() { FAB.updateData() } } // 2 - Good Code class Network { func updateData() { FAB.updateData() } } class MyClass1 { init() { Network.updateData() } } class MyClass2 { init() { Network.updateData() } } class MyClass3 { init() { Network.updateData() } }
在选项1中,如果想要替换Firebase,需要切换出三个函数进行调用。在选项2中,只需要更新自己的网络类。
7. guard let
除了解包选项,使用保护语句在其他方面也颇具实用性。可利用这些语句进行简单的条件验证检查,以便在某些条件下将程序控制转移到范围之外。
例如:
// example 1 - nil checking func checkTheText() { guard let text = textField.text else { return } //we made it this far... now we can use text for something! updateLabel(text) } // example 2 - conditional checking func conditionalCheck() { let c1 = true let c2 = false let c3 = true let c4 = true guard c1 c2 c3 c4 else { return } } // example 3 - multiple validation checks func validatePassword(_ password: String) -> Bool { guard password.count >= 8 else { return false } guard password.count <= 15 else { return false } guard checkPasswordCharacters(password) else { return false } //must contain capital letter special character etc... //password is valid return true }
8. 循环
// while loop var i = 0 while 5 > i { print(i) //output: 0 1 2 3 4 i = 1 } // repeat var a = 0 repeat { print(a) //output: 0 1 2 3 4 a = 1 } while a < 5 // for loop for c in 0...5 { print(c) //output: 0 1 2 3 4 5 } // for loop (no variable) for _ in 0...3 { print("count up") //output: count up count up count up count up } // for loop (less than equal than) for d in 0..<5 { print(d) //output: 0 1 2 3 4 } // for loop (reversed) for z in (1..<10).reversed() { print(z) //output: 9 8 7 6 5 4 3 2 1 } // for loop (stride) for g in stride(from: 1 to: 10 by: 3) { print(g) //output: 1 4 7 } // for loop (stride reversed) for k in stride(from: 3 to: 0 by: -1) { print(k) //output: 3 2 1 }
明白易懂。多种用于创建循环的句法,并在旁边列出相关输出。
9. 使用枚举确保切换语句/不同类别的项是类型安全的
// 1 - Ugly code var marketShare: Int! let operatingSystem = "iOS" switch operatingSystem { case "iOS": marketShare = 30 case "android": marketShare = 45 case "windows": marketShare = 15 case "sailfish": marketShare = 8 case "ubuntu": marketShare = 2 default: marketShare = 0 } // 2 - Clean code enum OS { case iOS android windows sailfish ubuntu } var marketShare_: Int! let operatingSystem_ = OS.iOS switch operatingSystem_ { case .iOS: marketShare_ = 30 case .android: marketShare_ = 45 case .windows: marketShare_ = 15 case .sailfish: marketShare_ = 8 case .ubuntu: marketShare_ = 2 }
在选项1中,可能会在switch语句中输入一个不合适的字符串,会导致市场份额的设置值不合理。
在选项2中,我们强制此switch语句是类型安全的,因此无法输入错误值并进行代码编译。
10. 使用回调发送完成处理程序
// 1 - Completion handlers func myFunction(completion: @escaping() -> ()) { UIView.animate(withDuration: 2 animations: { //run animation } completion: { _ in completion() }) } // 2 - Sending data through a callback: update UI upon network call class Modal { func getData(completion: ((_ data: String) -> Void)) { let data = "Network data!" completion(data) } } class ViewController: UIViewController { let model = Model() override func viewDidLoad() { super.viewDidLoad() model.getData { [weak self] (data: String) in self?.updateView(data) } } private func updateView(_ data: String) { print(data) } }
从选项1和选项2中可发现,可以在完成动作(动画、网络调用等)后发送警报,或者发送包含数据的警报。
11. 提供默认值
// "Hello World!" represents the default text we should use if the user's textInput is nil // 1 - Ugly Code var textInput: String? var text = "" if let t = textInput { text = t } else { text = "Hello World!" } // 2 - Clean code let text_ = textInput ?? "Hello World!"
在该例中,可发现两个用于设置变量值的选项,具体取决于用户输入是否为nil。
12. 为便于访问请将通用常量存储在一个文件中
为便于使用,笔者喜欢将静态常量存储在一个文件中,如下所示:
import Foundation struct Constants { struct Colors { static let blue = UIColor(hex: 0x111111) static let green = UIColor(hex: 0x222222) static let red = UIColor(hex: 0x333333) } struct Names { static let myName = "Gavin" static let myMomsName = "Marie" static let myDadsName = "Jake" static let mySistersName = "Jennifer" } }
例如,访问UIColor:
let myColorPick = Constants.Colors.green
let sistersName = Constants.Names.mySistersName
13. 自动参考计数
强烈建议阅读有关ARC(自动参考计数)的官方Swift文档。
Swift使用ARC跟踪和管理内存。这一点在使用应用程序中的几种情况下需要牢记。
简要介绍ARC在对象去初始化方面的影响:
试想,我们有Person这个类,Person:
class Person {
init() { print("initialized!") }
deinit { print("deinitialized!") }
}
接下来,创建三个变量。由于这三个变量是可选项,因此初始值为nil:
var ref1: Person? // nil
var ref2: Person? // nil
var ref3: Person? // nil
接下来,创建一个新的Person实例并将其分配至ref1。
ref1 = Person() // console output: "initialized!"
然后,指定ref2和ref3作为同一Person对象的参考:
ref2 = ref1 // Person
ref3 = ref1 // Person
既然所有三个参考都指向同一个Person对象,那么我们可以将前两个参考设置为nil,同时仍将Person对象保留在内存中,如下所示:
ref1 = nil
ref2 = nil
最后,要对Person对象去初始化,请将第三个和最后一个参考设置为nil:
ref3 = nil // console output: "deinitialized!"
14. 为函数参数提供默认参数
func printToConsole(_ messageLine1: String _ messageLine2: String = "This is line 2!") { print("\(messageLine1) | \(messageLine2)") } printToConsole("This is line 1!") // This is line 1! | This is line 2! printToConsole("This is line one." "This is line two.") //This is line one. | This is line two.
由上可见,为输入参数提供默认值非常简单。
15. 通过UserDefaults15.49/5000从内存中编码/解码结构
import Foundation // - represents a single Task struct TaskItem: Codable { var isToggledOn: Bool var title: String var notes: String } // - handles on-device memory retrieval and storage class MemoryManager { static var tasks: [TaskItem]! // - static array of TaskItems that currently exists on the device private let defaults = UserDefaults.standard // - reference to application's UserDefaults dictionary private let DEFAULTS_KEY = "TASK_LIST" // - the key we use to retrieve/save our array of TaskItems init() { MemoryManager.tasks = [TaskItem]() retrieveData() saveData() } // - decode our array from memory private func retrieveData() { // - check if an array of TaskItems already exists in memory var didFail = false if let data = UserDefaults.standard.value(forKey: DEFAULTS_KEY) as? Data { if let tasks = try? PropertyListDecoder().decode(Array<TaskItem>.self from: data) { MemoryManager.tasks = tasks } else { didFail = true } } else { didFail = true } // - guard statement: if we had a failure then continue guard didFail else { return } // - we had a failure in finding a pre-existing array create a new array of TaskItems! MemoryManager.tasks = [ TaskItem(isToggledOn: false title: "task 1" notes: "this is task 1") TaskItem(isToggledOn: false title: "task 2" notes: "this is task 2") TaskItem(isToggledOn: true title: "task 3" notes: "this is task 3") TaskItem(isToggledOn: false title: "task 4" notes: "this is task 4") TaskItem(isToggledOn: false title: "task 5" notes: "this is task 5") TaskItem(isToggledOn: true title: "task 6" notes: "this is task 6") ] } // - encode our array into memory private func saveData() { UserDefaults.standard.set(try? PropertyListEncoder().encode(MemoryManager.tasks) forKey: DEFAULTS_KEY) } }
此文件演示了许多实用性功能。
在顶部,有一个标题为TaskItem的struct,符合Codable;这种一致性允许我们通过序列化格式(如JSON)对该结构进行编码/解码。
之后可以发现,在函数retrieveData()中,使用了guard语句和if let语句检查UserDefault是否存在一个预先存在的TaskItem数组。
如果不存在这样的数组,那么会创建一个包括上述项目的新数组。
在该文件底部,可看到如何通过PropertyListEncoder、字典键和可选的try block块将现有的Codable项目数组编码到内存中的演示。
此文件的主要用例发生在应用程序的初始化运行阶段。
在此阶段,检查需要存储的预存在项目数组。如果此数组不存在,那么可以预先使用项目数组进行填充,然后将其保存到内存中供以后调用。
留言 点赞 关注
我们一起分享AI学习与发展的干货
编译组:梁晶晶、胡昕彤
相关链接:
https://medium.com/better-programming/15-quick-tips-to-improve-your-swift-code-ed390c99afcd
如需转载,请后台留言,遵守转载规范