Swift 常见问题

Posted 颐和园

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift 常见问题相关的知识,希望对你有一定的参考价值。

比较对象地址

可以用 Objectidentifier 结构来唯一标志一个对象:

ObjectIdentifier(Person(name: "James")).uintValue) 

通过比较 UnintValue 即可得知是否是同一对象。

协议扩展

protocol BannerViewProtocol: class {
var bannerView: BannerView? { get set }
var containerStackView: UIStackView { get set } 
}
extension BannerViewProtocol where Self: UIViewController{ 
func showBanner( banner: BannerView, animated: Bool, animationDuration: Double) { 
bannerView?.removeFromSuperview() 
......
containerStackView.insertArrangedSubview(banner, at: 0) 
...... 
} 

注意,通过协议扩展,实现让协议也能够实现具体方法(等同于继承),并调用存 储属性。在 showBanner 方法中调用了协议中定义的 bannerView 属性。
当然你还需要让具体的类继承该协议并实现协议中的两个属性:

public class ViewController: BannerViewProtocol { ...... 
weak var bannerView: BannerView?
lazy var containerStackView: UIStackView = { 
...... 
}() 

协议和关联类型

在 protocol 中不支持泛型参数,它通过 assocaitedtype 来支持泛型,比如:

protocol Screen {
associatedtype ItemType
var items: [ItemType] { get set } 
} 

相当于(伪代码):

protocol Screen<T> {
var items: [T] { get set } 
} 

这样,实现类可以通过 ItemType 来指定关联类型的具体类型,比如:

class MainScreen: Screen { 
typealias ItemType = String var items = [String]() 
}

其中对 typealias ItemType 赋值时只能用具体的类,而不能是 protocol。
此外, typealias ItemType = String 可以省略,因为编译器会自动推断关联类型。

subdata 和 dropFirst

let array : [UInt8] = [0,1,2,3,4,5,6,7,8,9,10,11,12] let data = Data(array)
let datadropped = data.dropFirst(2) 
let sub = data.subdata(in: 4..<8 ) // gives 4,5,6,7
let sub2 = datadropped.subdata(in: 4..<8) // also gives 4,5,6,7 

datadropped是一个 Slice。
它包含数据的子集,但与原始集合共享相同的索引。
它崩溃是因为 datadropped 的第一个索引是 2,而不是 0。 要获得新的 Data 对象,必须这样写:

let datadropped = Data(data.dropFirst(2)) 

Dynamic font

textField.adjustsFontForContentSizeCategory = true 

然后将控件的 font 全部设置为动态字体:

textField.font = .preferredFont(forTextStyle: .body) 

dynamic font, 需要重启 app 才能看到效果

你需要监听 UIContentSizeCategory.didChangeNotification 通知,收到通知后重新设置 UI 组件的动态字体属性。

map 遍历时提供 index

let items = payeeList.enumerated().compactMap { (index, item) in generatePayeeItemViewConfig(payee: item, isSelected: 
selectedIndex?.section == 1 && index == selectedIndex?.row) } 

关于 radioButton/checkButton 的 accessibility

首先设置 id:

radioButton.accessibilityIdentifier = “radioButton"

将 radioButton 添加到 accessibility elements:

accessibilityElements = [titleLabel, descLabel, amountLabel, radioButton] 

当 isSelected 被改变时修改 accessibility label:

func configView(
accessibilityProvider: AccessibilityStringProvidable, selected: Bool = false 
){ ...... 
radioButton.isSelected = selected 
radioButton.accessibilityLabel = selected ? accessibilityProvider.commonRadioselected() : accessibilityProvider.commonRadioUnselected() 
} 

允许用户在 textview/textfield 的任何位置放置光标

从 iOS 7 开始,你想文本编辑框的任意位置放置光标需要更多的技巧,比如⻓按光
标然后拖动或者 Trackpad 模式(⻓按空格键)。 因为系统默认会对输入内容进行单词识别,在输入框中通过点击来调整光标位置
时,系统会自动将光标放置在单词之前或之后。 要改变这种默认行为相当麻烦,并且效果也不理想。你需要:

1、扩展 UITextView

class WCTextView: UITextView {
public var shouldMoveCursor = false
public var recommendedCursorPosition = UITextPosition() 
public func moveCursorToRecommendedPosition() {
let range = textRange(from: recommendedCursorPosition, to: 
recommendedCursorPosition) selectedTextRange = range 
} 
override public func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? { 
let deltaY = contentOffset.y
let detltaPoint = CGPoint(x: point.x, y: point.y + deltaY) 
recommendedCursorPosition = closestPosition(to: detltaPoint) ?? UITextPosition() 
shouldMoveCursor = true 
return super.hitTest(point, with: event) } 
} 

2、使用扩展类

let textView = WCTextView() textView.delegate = self 

3、实现委托

public func textView(_ textView: UITextView, shouldChangeTextIn range: 
NSRange, replacementText text: String) -> Bool { if !text.isEmpty { 
self.textView.shouldMoveCursor = false } 
return true 
} 
public func textViewDidChangeSelection(_ textView: UITextView) {
if textView.selectedRange.length == 0, self.textView.shouldMoveCursor { 
self.textView.moveCursorToRecommendedPosition() 
self.textView.shouldMoveCursor = false } 
} 

软键盘添加 Done button

extension UITextField {
func addDoneToolbar(onDone: (target: Any, action: Selector)? = nil) { 
var doneButton: UIBarButtonItem! 
if let onDone = onDone { 
doneButton = UIBarButtonItem(title: localizedString(with: "cosmos_common_done"), style: .done, target: onDone.target, action: onDone.action) 
} else {
doneButton = UIBarButtonItem(title: localizedString(with: 
"cosmos_common_done"), style: .done, target: self, action: #selector(resignFirstResponder)) 
} 
let toolbar = UIToolbar() toolbar.barStyle = .default toolbar.items = [ 
UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: self, action: nil), 
doneButton ] 
toolbar.sizeToFit() 
inputAccessoryView = toolbar } 
} 

用法:

accountNumberField.addDoneToolbar() 

或者:

viewController.accountNumberInputView?.addDoneToolbar(onDone: (target: self, action: #selector(doneTap))) 

无法校验单引号和双引号

无论正则表达式怎么写,你都无法在 swift 里成功验证单引号和双引号。是因为
Xcode 里的单引号和双引号和 iOS 里的单引号、双引号不是一个字符。 例如:

"^[a-zA-Z0-9#%&()+ ,\\\\-./:;=?\\\\[\\\\]^_|*\\ ‘\\” ]*$"

在 iphone 上这两个符号实际上是:‘ 和 “,Unicode 码分别是: \\U8216 和 \\U8220

在 lldb 中打印这两个符号的 unicode 的方法如下:

(lldb) ex inputText.unicodeScalars 
(String.UnicodeScalarView) $R4 = { _guts = { 
_object = (_countAndFlagsBits = 1, _object = 0x500060000286c620) } 
} 
(lldb) po Int($R4[$R4.startIndex].value) 
8220 

因此上述表达式应写为:

"^[a-zA-Z0-9#%&()+ ,\\\\-./:;=?\\\\[\\\\]^_|*\\ ‘’'“”\\"]*$" 

以上是关于Swift 常见问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Swift 使用此代码片段为 iOS 应用程序初始化 SDK?

swift 代码片段

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

Swift新async/await并发中利用Task防止指定代码片段执行的数据竞争(Data Race)问题

swift 为什么我们有一个片段。我认为这有助于我们在另一个页面中有一个代码。

swift BaasBox和Swift的片段 - 第2部分