1、swift中使用標記(oc中的#pragma mark)
// MARK: 你的標記
...
// TODO: 你的待辦
...
// FIXME: 待修復的內容
...
2、for...in遍歷數組同時拿到下標和對應的元素
for (index, element) in arr.enumerated() {
print("index: \(index), element: \(element)")
}
3、UIButton默認圖片在左文字在右,有時候需要交換圖片和文字位置(圖片在右文字在左)
- 方法1,semanticContentAttribute屬性,要求iOS9+
button.semanticContentAttribute = .forceRightToLeft
- 方法2,修改titleEdgeInsets和imageEdgeInsets
button.sizeToFit()
button.titleEdgeInsets = UIEdgeInsets(top: 0, left: -button.imageView!.frame.size.width, bottom: 0, right: button.imageView!.frame.size.width)
button.imageEdgeInsets = UIEdgeInsets(top: 0, left: button.titleLabel!.frame.size.width, bottom: 0, right: -button.titleLabel!.frame.size.width)
- 方法3,直接改transform
button.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.titleLabel?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
button.imageView?.transform = CGAffineTransform(scaleX: -1.0, y: 1.0)
- 方法4,子類化UIButton,重寫 func layoutSubviews() 方法
class WZBButton: UIButton {
override func layoutSubviews() {
super.layoutSubviews()
/// 文字在左,圖片在右
if let imageView = imageView, let titleLabel = titleLabel, titleLabel.frame.minX > imageView.frame.minX {
var imageViewFrame = imageView.frame
var titleLabelFrame = titleLabel.frame
titleLabelFrame.origin.x = imageViewFrame.minX
imageViewFrame.origin.x = titleLabelFrame.maxX
imageView.frame = imageViewFrame
titleLabel.frame = titleLabelFrame
}
}
}
- 方法5,子類化UIButton,重寫 func titleRect(forContentRect contentRect: CGRect) -> CGRect 和 func imageRect(forContentRect contentRect: CGRect) -> CGRect 方法
class WZBButton: UIButton {
override func titleRect(forContentRect contentRect: CGRect) -> CGRect {
var rect = super.titleRect(forContentRect: contentRect)
rect.origin.x = 0
return rect
}
override func imageRect(forContentRect contentRect: CGRect) -> CGRect {
var rect = super.imageRect(forContentRect: contentRect)
rect.origin.x = contentRect.maxX - rect.width
return rect
}
}
5、用weak修飾protocol
如果這樣寫,會報錯
protocol MyProtocol {
func test()
}
class MyClass {
/// 報錯信息
/// 'weak' must not be applied to non-class-bound 'MyProtocol'; consider adding a protocol conformance that has a class bound
weak var delegate: MyProtocol?
}
正確的寫法是讓這個協議繼承AnyObject(這樣寫之后,這個協議就只能被類遵守),修改后的寫法:
/// 正確寫法
protocol MyProtocol: AnyObject {
func test()
}
class MyClass {
weak var delegate: MyProtocol?
}
6、像UITableView的tableHeaderVIew一樣為UICollectionview添加headerView
let headerHeight = 100
collectionView.contentInset = UIEdgeInsets(top: headerHeight, left: 0, bottom: 0, right: 0)
headerView = YourHeaderView()
headerView?.frame = CGRect(x: 0, y: -headerHeight, width: collectionView.width, height: headerHeight)
7、Int和String互相轉換
/// Int to String
let i = 10
let str = String(i)
print("String is : \(str)")
/// String to Int
let num = Int(str)
/// 因為轉換過來是可選的,所以需要先嘗試解包
if let num = num {
print("Int is : \(num)")
}
8、系統UIButton設置文字的時候會有動畫效果,如何關閉動畫效果
- 方法一、禁用UIView動畫
UIView.setAnimationsEnabled(false)
button.setTitle(title, for: .normal)
button.layoutIfNeeded()
UIView.setAnimationsEnabled(true)
- 方法二、使用performWithoutAnimation方法
UIView.performWithoutAnimation {
button.setTitle(title, for: .normal)
button.layoutIfNeeded()
}
9、查看本地swift版本
swift版本是和Xcode版本一一對應的,一般來說打開終端運行,云心命令就能看到:
xcrun swift -version
我的Xcode版本是11.1,運行這個命令行打印的是:
Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
Target: x86_64-apple-darwin19.0.0
有種情況是你的電腦上安裝了多個Xcode(比如你安裝了某個Beta版本),這時候可以使用以下命令修改路徑:
sudo xcode-select -s /Applications/Xcode_Beta.app
10、項目中的swift版本
-
項目中指定使用swfit某個版本
選擇target -> build settings -> 搜索swift_version -> 選擇指定版本
指定使用swfit某個版本 代碼中判斷swift版本
#if swift(>=5.2)
print("Swift版本 >= 5.2")
#elseif swift(>=5.1)
print("Swift版本 >= 5.1")
#elseif swift(>=5.0)
print("Swift版本 >= 5.0")
#elseif swift(>=4.2)
print("Swift版本 >= 4.2")
#elseif swift(>=4.1)
print("Swift版本 >= 4.1")
#elseif swift(>=4.0)
print("Swift版本 >= 4.0")
#elseif swift(>=3.2)
print("Swift版本 >= 3.2")
#elseif swift(>=3.0)
print("Swift版本 >= 3.0")
#elseif swift(>=2.2)
print("Swift版本 >= 2.2")
#elseif swift(>=2.1)
print("Swift版本 >= 2.1")
#elseif swift(>=2.0)
print("Swift版本 >= 2.0")
#elseif swift(>=1.2)
print("Swift版本 >= 1.2")
#elseif swift(>=1.1)
print("Swift版本 >= 1.1")
#elseif swift(>=1.0)
print("Swift版本 >= 1.0")
#endif
11、數組合并
- 方法一
let a = [1, 2, 3]
let b = [4, 5, 6]
let c = a + b
print(c)
- 方法二
var a = [1, 2, 3]
let b = [4, 5, 6]
a.append(contentsOf: b)
print(a)
- 方法三
var a = [1, 2, 3]
let b = [4, 5, 6]
a += b
print(a)
12、UIViewController點擊空白處隱藏鍵盤
- 方法一,在ViewController中重寫touchBegin方法
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
view.endEditing(true)
}
- 方法二,在UIViewController的分類中為View添加一個單擊手勢
private var hideKeyboardWhenTappedAroundKey: UInt8 = 0
private var tapGestureKey: UInt8 = 0
extension UIViewController {
var hideKeyboardWhenTappedAround: Bool {
get { (objc_getAssociatedObject(self, &hideKeyboardWhenTappedAroundKey) as? Bool) ?? false }
set {
objc_setAssociatedObject(self, &hideKeyboardWhenTappedAroundKey, newValue, .OBJC_ASSOCIATION_ASSIGN)
if newValue {
if tapGesture == nil {
tapGesture = UITapGestureRecognizer(target: self, action: #selector(hiddenKeyBoard))
view.addGestureRecognizer(tapGesture!)
}
} else {
if let tapGesture = tapGesture {
view.removeGestureRecognizer(tapGesture)
self.tapGesture = nil
}
}
}
}
private var tapGesture: UITapGestureRecognizer? {
get { objc_getAssociatedObject(self, &tapGestureKey) as? UITapGestureRecognizer }
set { objc_setAssociatedObject(self, &tapGestureKey, newValue, .OBJC_ASSOCIATION_RETAIN) }
}
@objc private func hiddenKeyBoard() {
view.endEditing(true)
}
}
然后在需要的控制器中:
override func viewDidLoad() {
super.viewDidLoad()
self.hideKeyboardWhenTappedAround = true
}
13、獲取當前設備型號(包括iPhone、iPad、iPod、Apple TV、HomePod和模擬器)
- 方法一,在ViewController中重寫touchBegin方法
public extension UIDevice {
static let modelName: String = {
var systemInfo = utsname()
uname(&systemInfo)
let machineMirror = Mirror(reflecting: systemInfo.machine)
let identifier = machineMirror.children.reduce("") { identifier, element in
guard let value = element.value as? Int8, value != 0 else { return identifier }
return identifier + String(UnicodeScalar(UInt8(value)))
}
func mapToDevice(identifier: String) -> String {
#if os(iOS)
switch identifier {
case "iPod5,1": return "iPod touch (5th generation)"
case "iPod7,1": return "iPod touch (6th generation)"
case "iPod9,1": return "iPod touch (7th generation)"
case "iPhone3,1", "iPhone3,2", "iPhone3,3": return "iPhone 4"
case "iPhone4,1": return "iPhone 4s"
case "iPhone5,1", "iPhone5,2": return "iPhone 5"
case "iPhone5,3", "iPhone5,4": return "iPhone 5c"
case "iPhone6,1", "iPhone6,2": return "iPhone 5s"
case "iPhone7,2": return "iPhone 6"
case "iPhone7,1": return "iPhone 6 Plus"
case "iPhone8,1": return "iPhone 6s"
case "iPhone8,2": return "iPhone 6s Plus"
case "iPhone9,1", "iPhone9,3": return "iPhone 7"
case "iPhone9,2", "iPhone9,4": return "iPhone 7 Plus"
case "iPhone8,4": return "iPhone SE"
case "iPhone10,1", "iPhone10,4": return "iPhone 8"
case "iPhone10,2", "iPhone10,5": return "iPhone 8 Plus"
case "iPhone10,3", "iPhone10,6": return "iPhone X"
case "iPhone11,2": return "iPhone XS"
case "iPhone11,4", "iPhone11,6": return "iPhone XS Max"
case "iPhone11,8": return "iPhone XR"
case "iPhone12,1": return "iPhone 11"
case "iPhone12,3": return "iPhone 11 Pro"
case "iPhone12,5": return "iPhone 11 Pro Max"
case "iPad2,1", "iPad2,2", "iPad2,3", "iPad2,4":return "iPad 2"
case "iPad3,1", "iPad3,2", "iPad3,3": return "iPad (3rd generation)"
case "iPad3,4", "iPad3,5", "iPad3,6": return "iPad (4th generation)"
case "iPad6,11", "iPad6,12": return "iPad (5th generation)"
case "iPad7,5", "iPad7,6": return "iPad (6th generation)"
case "iPad7,11", "iPad7,12": return "iPad (7th generation)"
case "iPad4,1", "iPad4,2", "iPad4,3": return "iPad Air"
case "iPad5,3", "iPad5,4": return "iPad Air 2"
case "iPad11,4", "iPad11,5": return "iPad Air (3rd generation)"
case "iPad2,5", "iPad2,6", "iPad2,7": return "iPad mini"
case "iPad4,4", "iPad4,5", "iPad4,6": return "iPad mini 2"
case "iPad4,7", "iPad4,8", "iPad4,9": return "iPad mini 3"
case "iPad5,1", "iPad5,2": return "iPad mini 4"
case "iPad11,1", "iPad11,2": return "iPad mini (5th generation)"
case "iPad6,3", "iPad6,4": return "iPad Pro (9.7-inch)"
case "iPad6,7", "iPad6,8": return "iPad Pro (12.9-inch)"
case "iPad7,1", "iPad7,2": return "iPad Pro (12.9-inch) (2nd generation)"
case "iPad7,3", "iPad7,4": return "iPad Pro (10.5-inch)"
case "iPad8,1", "iPad8,2", "iPad8,3", "iPad8,4":return "iPad Pro (11-inch)"
case "iPad8,5", "iPad8,6", "iPad8,7", "iPad8,8":return "iPad Pro (12.9-inch) (3rd generation)"
case "AppleTV5,3": return "Apple TV"
case "AppleTV6,2": return "Apple TV 4K"
case "AudioAccessory1,1": return "HomePod"
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "iOS"))"
default: return identifier
}
#elseif os(tvOS)
switch identifier {
case "AppleTV5,3": return "Apple TV 4"
case "AppleTV6,2": return "Apple TV 4K"
case "i386", "x86_64": return "Simulator \(mapToDevice(identifier: ProcessInfo().environment["SIMULATOR_MODEL_IDENTIFIER"] ?? "tvOS"))"
default: return identifier
}
#endif
}
return mapToDevice(identifier: identifier)
}()
}
調用的時候:
print("當前設備為:\(UIDevice.modelName)")
14、獲取當前App版本號
if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] {
print("當前App版本號:\(version)")
}
15、protocol中創建可選方法
- 方法一,在分類中使用默認實現
protocol MyProtocol {
func test()
}
extension MyProtocol {
/// 這里有了默認實現之后
/// 這個方法對于其他遵守MyProtocol的類來說就是可選的了
/// 可以實現也可以不實現
func test() { }
}
- 方法二,使用@objc optional
@objc protocol MyProtocol {
@objc optional func test()
}
不建議用方法二,它的弊端是整個protocol需要用@objc修飾,這就破壞了swift中protocol的優勢,使用@objc之后,這個協議只能被類準守,不能被結構體和枚舉準守了。還有個缺點是一旦這樣寫,又會回到OC時代調用協議的方法時要先判斷是否實現了這個方法,這樣很麻煩。因此建議用方法一
16、數組轉字符串
var a = ["1", "2", "3"]
print(a.joined(separator: "-")) // "1-2-3"
注意:數組的joined方法只當數組中的元素類型為字符串的時候可以調用
如果數組中的元素不是字符串,可以像下邊這樣先轉化一下:
var a = [1, 2, 3]
let b = a.map({ $0.description })
print(b.joined(separator: "-")) // "1-2-3"
17、URL轉String
let url = URL(string: "https://baidu.com")
let stringUrl = url?.absoluteString
NSURL同樣使用absoluteString屬性轉換
18、==和===的區別
- ==是比較雙方的值是否相等,強調的是值相等,比如:
let a = 1
let b = 1
print(a == b) // true
如果是基本數據類型是可以用==直接比較的,但如果是自定義的枚舉、結構體和類,就需要實現Equatable協議,并且重寫==運算符方法,代碼如下:
class Person: Equatable {
var id: Int
init(id: Int) {
self.id = id
}
static func ==(lhs: Person, rhs: Person) -> Bool { lhs.id == rhs.id }
}
let p1 = Person(id: 100000)
let p2 = Person(id: 100000)
print(p1 == p2) // true
- ===是比較雙方的指針是否指向同一個實例,只能用來比較引用類型,因為引用類型才涉及到指針指向的問題:
class A { }
let a1 = A()
let a2 = A()
let a3 = a1
/// a1和a2的地址指向的是不同的內存
print(a1 === a2) // false
/// a1和a3的地址指向的是相同的內存
print(a1 === a3) // true
19、生成隨機的UUID字符串
/// uuid is : 59373CDA-F5FF-4D16-B85A-E3F1A5542F8A
print("uuid is : \(UUID().uuidString)")
20、字符串的截取
- 直接使用str[startIndex...endIndex]
let str = "1234567"
let index = str.index(str.startIndex, offsetBy: 3)
let newStr = String(str[..<index])
print("前三個字符:\(newStr)") // 前三個字符:123
let index1 = str.index(str.endIndex, offsetBy: -3)
let newStr1 = String(str[index1...])
print("后三個字符:\(newStr1)") // 后三個字符:567
let startIndex = str.index(str.startIndex, offsetBy: 2)
let endIndex = str.index(str.startIndex, offsetBy: 4)
let newStr2 = String(str[startIndex...endIndex])
print("第3到第5個字符:\(newStr2)") // 第3到第5個字符:345
- 使用的時候最好封裝到分類中
extension String {
func substring(start: Int, end: Int) -> String {
let startIndex = self.index(self.startIndex, offsetBy: start)
let endIndex = self.index(self.startIndex, offsetBy: end)
return String(self[startIndex...endIndex])
}
}
使用的時候直接調用substring方法:
let str = "1234567"
print("前3個字符:\(str.substring(start: 0, end: 2))") // 前3個字符:123
print("第3到第5個字符:\(str.substring(start: 2, end: 4))") // 第3到第5個字符:345
21、設置UILabel的行間距
- 方法一,使用xib
- 方法二,使用代碼
extension UILabel {
/// 設置行間距和行高
/// - Parameters:
/// - lineSpacing: 行間距
/// - lineHeightMultiple: 行高
func setLineSpacing(lineSpacing: CGFloat = 0.0, lineHeightMultiple: CGFloat = 0.0) {
guard let labelText = self.text else { return }
let paragraphStyle = NSMutableParagraphStyle()
paragraphStyle.lineSpacing = lineSpacing
paragraphStyle.lineHeightMultiple = lineHeightMultiple
let attributedString:NSMutableAttributedString
if let labelattributedText = self.attributedText {
attributedString = NSMutableAttributedString(attributedString: labelattributedText)
} else {
attributedString = NSMutableAttributedString(string: labelText)
}
attributedString.addAttribute(.paragraphStyle, value:paragraphStyle, range:NSMakeRange(0, attributedString.length))
self.attributedText = attributedString
}
}
使用:
label.text = "我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試\n文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本我是測試文本"
label.setLineSpacing(lineSpacing: 5)
22、UIPanGestureRecognizer只是別垂直方向或者水平方向
- 實現UIGestureRecognizerDelegate中的func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool方法
override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
let velocity = pan.velocity(in: contentView)
if 只是別垂直 { return abs(velocity.y) > abs(velocity.x) }
if 只是別水平 { return abs(velocity.x) > abs(velocity.y) }
return true
}
23、判斷當前設備是模擬器還是真機
#if targetEnvironment(simulator)
print("當前設備為模擬器")
#else
print("當前設備為真機")
#endif
24、在初始化的過程中調用某個屬性的didSet方法
- 在初始化方法中使用defer延時賦值
class A {
var i = 0 {
didSet {
print(i)
}
}
init(i: Int) {
defer { self.i = i }
}
}
_ = A(i: 10)
25、在開發中經常遇到數組越界導致的崩潰,可以利用swift的可選值特性防止這種問題發生
extension Array {
subscript(safe index: Index) -> Element? {
indices.contains(index) ? self[index] : nil
}
}
使用:
let arr = [1, 2, 3]
print(arr[safe: 5]) /// nil
26、判斷當前設備是iPhone還是iPad
func isPad() -> Bool { UIDevice.current.userInterfaceIdiom == .pad }
func isPhone() -> Bool { UIDevice.current.userInterfaceIdiom == .phone }
print(isPhone()) // true
print(isPad()) // false
27、字符串中使用換行
- 方法一
let str = """
hello world!
hello swift!
"""
- 方法二
let str = "hello world!\n" + "hello swift!"
28、數組去重
老問題了,方法有很多,我這里列舉幾個我認為簡單并且常用的方法:
- 方法一,分類中給Array添加方法,手動遍歷過濾
extension Array where Element: Hashable {
func removeDuplicates() -> [Element] {
var result: [Element] = []
for element in self {
if !result.contains(element) {
result.append(element)
}
}
return result
}
}
let arr = [1, 3, 4, 4, 4]
print(arr.removeDuplicates()) // [1, 3, 4]
- 方法二,利用Set元素不能重復的特性,但這種方法不能保證數組順序
let arr = [1, 3, 4, 4, 4]
print(Array(Set(arr))) // [3, 1, 4]
- 方法三,根據條件過濾(不推薦使用,時間復雜度較高)
extension Array {
func removeDuplicates(where predicate: (_ lhs: Element, _: Element) -> Bool) -> [Element] {
var result: [Element] = []
forEach { (element) in
if !result.contains(where: { predicate($0, element) }) {
result.append(element)
}
}
return result
}
}
struct P {
var name: String
var age: Int
}
var p = P(name: "wzb", age: 18)
var p1 = P(name: "wzb", age: 19)
var p2 = P(name: "wzb", age: 19)
var p3 = P(name: "wzb", age: 15)
// (name: "wzb", age: 18)、(name: "wzb", age: 19)、(name: "wzb", age: 15)
print([p, p1, p2, p3].removeDuplicates(where: { $0.age == $1.age }))
29、使用系統自帶下拉刷新控件
很多人可能還不知道iOS其實是有自帶的下拉刷新控件的,名字叫UIRefreshControl,首先看一下API:
// 獲取是否正在刷新
open var isRefreshing: Bool { get }
// 設置菊花的顏色
open var tintColor: UIColor!
// 設置標題的富文本
open var attributedTitle: NSAttributedString?
// 開始刷新
open func beginRefreshing()
// 結束刷新
open func endRefreshing()
使用方法如下:
- 方法一,代碼方式
let refreshControl = UIRefreshControl()
refreshControl.addTarget(self, action: #selector(refreshAction), for: .valueChanged)
// UIScrollView/UITableView/UICollectionView
scrollView.refreshControl = refreshControl
@objc func refreshAction() {
// 刷新數據
...
// 結束刷新
refreshControl?.endRefreshing()
}
- 方法二,xib方式
-
在xib中拖入UITableViewController,在屬性列表中把refreshing設置為Enabled,支持設置一個title,以及title的字體和align
xib配置
-
然后需要在代碼中添加監聽事件:
override func viewDidLoad() {
super.viewDidLoad()
refreshControl?.addTarget(self, action: #selector(refreshAction), for: .valueChanged)
}
@objc func refreshAction() {
// 刷新數據
...
// 結束刷新
refreshControl?.endRefreshing()
}
有點需要注意,UIScrollView/UITableView/UICollectionView在iOS才增加了refreshControl屬性,因此如果你的app兼容iOS 10以下,在添加這個控件的時候需要加入判斷:
if #available(iOS 10.0, *) {
scrollView.refreshControl = refreshControl
} else {
scrollView.addSubview(refreshControl)
}
30、在oc中用isKindOfClass方法判斷一個對象是否為某個類,或者繼承某個類,那在swift中如何使用isKindOfClass呢
- 方法一
// oc
if ([view isKindOfClass: UILabel.class]) {
// view是UILabel類型
}
// swift
if view.isKind(of: UILabel.self) {
// view是UILabel類型
}
- 方法二
// oc
if ([view isKindOfClass: UILabel.class]) {
// view是UILabel類型
}
// swift
if let _ = view as? UILabel {
// view是UILabel類型
}
- 方法三
// oc
if ([view isKindOfClass: UILabel.class]) {
// view是UILabel類型
}
// swift
if view is UILabel {
// view是UILabel類型
}
推薦使用方法二和方法三
31、多種方法刪除數組中的元素
- 刪除首個元素
var arr = [1, 2, 3]
let firstValue = arr.removeFirst() // 1
print(arr) // [2, 3]
- 刪除末尾元素
var arr = [1, 2, 3]
let lastValue = arr.removeLast() // 3
print(arr) // [1, 2]
- 刪除所有元素
var arr = [1, 2, 3]
arr.removeAll()
print(arr) // []
- 刪除第n個元素
var arr = [1, 2, 3]
let value = arr.remove(at: 1) // 2
print(arr) // [1, 3]
- 刪除某個已知的元素
var arr = [1, 2, 3]
if let index = arr.firstIndex(of: 2) {
arr.remove(at: index)
}
print(arr) // [1, 3]
- 通過range批量刪除
var arr = [1, 2, 3, 4, 5]
// 刪除第2、3、4個元素
arr[1...3] = []
print(arr) // ["c", "e"]
- 用過濾的方法一次刪除多個元素
var arr = [1, 2, 3, 4]
// 只保留大于2的,其他的刪除
arr = arr.filter{ $0 > 2 }
print(arr) // [3, 4]
- 用removeAll的方法一次刪除多個元素
var arr = [1, 2, 3, 4]
// 刪除元素小于等于2的元素
arr.removeAll{ $0 <= 2 }
print(arr) // [3, 4]
- 通過下標數組批量刪除元素
var arr = [1, 2, 3, 4, 5]
// 需要刪除的下標
let removeIndexs = [0, 1, 2]
arr = arr.enumerated().filter({ !removeIndexs.contains($0.offset) }).map({ $0.element })
print(arr) // [4, 5]
- 刪除另一個數組中的元素
var arr = ["a", "b", "c", "d", "e"]
let removeObjects = ["a", "b", "d"]
arr = arr.filter { !removeObjects.contains($0) }
print(arr) // ["c", "e"]
32、修改UITextField的占位文字顏色
- 方式一,代碼方式,利用富文本
textField.attributedPlaceholder = NSAttributedString(string: "請輸入用戶名", attributes: [NSAttributedString.Key.foregroundColor : UIColor.red])
-
方式二,xib方式,添加keyPath:placeholderLabel.textColor
xib方式占位文字顏色 方式三,兩者結合,用runtime為UITextField添加placeholderColor的屬性
private var placeholderColorKey: UInt8 = 0
extension UITextField {
@IBInspectable
var placeholderColor: UIColor? {
get { objc_getAssociatedObject(self, &placeholderColorKey) as? UIColor }
set {
attributedPlaceholder = NSAttributedString(string: "請輸入用戶名", attributes: [NSAttributedString.Key.foregroundColor : UIColor.red])
objc_setAssociatedObject(self, &placeholderColorKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}
代碼使用:
textField.placeholderColor = .red
xib使用:
33、關于不常用的高階函數Reduce
- Reduce可以將一組元素按照指定規則組合在一起生成一個新值,比如求一組數組之和:
var arr = [1, 2, 3, 4]
let sum = arr.reduce(0, +) // 10
其中,第一個參數是初始值,第二個參數是操作符,可以簡單理解為sum = 0 + 1 + 2 + 3 + 4
如果reduce函數的定義,第二個參數是個block,那上邊的這個例子展開后是這樣的:
var arr = [1, 2, 3, 4]
let sum = arr.reduce(0) { (result, value) -> Int in
// reduce會遍歷arr,重復調用這個block
// 將上次block產生的結果(第一次會把默認值傳進來)和本次的值傳入進來
// result是上次的結果,value遍歷本次數組取得的值
return result + value
}
- 按照這個思路,可以自己簡單實現一個reduce
extension Array {
func wzb_reduce(_ defaultValue: Element, _ eachOperation: ((Element, Element) -> Element)) -> Element? {
var result: Element?
for item in self {
result = eachOperation(result ?? defaultValue, item)
}
return result
}
}
let test_arr = ["A", "p", "p", "l", "e"]
let sum = test_arr.wzb_reduce("Hello ") { (result, value) -> String in
result + value
}
sum // "Hello Apple"
喜歡可以隨手點個喜歡或者關注一下哦!
您的支持是我最大的動力??!