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