Swift Tips(1-17)

Posted 颐和园

tags:

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

1. 为什么有时候无法为一个 View从别处粘贴进另一个View后无法添加约束?

在 Size 面板中将 Layout 属性改为 Inferred(Constraints)。此外,当一个 view 下面没有 Constraints 时,无法将别的 view 拖进这个 view。你可以从 Library 中随便拖一个控件到这个view,然后随便为这个控件添加一个约束,然后删除这个控件。这时就可以为这个 View 添加一个 Contraints 集合,然后就可以拖拽别的 view 到这个 view 来了。

2. 为什么从 xib 中加载的 view 不会随父 View 的大小变化?

在 xib 文件中一定不要直接在根 view 上直接拖放控件并布局,而是要在根 view 下面再建一个 view,然后在第二级的 view 上放控件和布局。也就是说,根 view 上的约束在 loadNib 后会全部失效,只有从第二级以下的 view 创建约束才是有效的。

3. 如何让自定义View 的 size 根据内容自增长?

答案是覆盖 intrinsicContentSize 属性。

class StateLabel: UIView {
override var intrinsicContentSize: CGSize {
return CGSize(width: titleLabel.intrinsicContentSize.width + Constants.imageViewHeight + Constants.horizontalPadding,
height: titleLabel.intrinsicContentSize.height)
}

如果需要重新计算 intrinsic content size,请调用 invalidateIntrinsicContentSize() 方法。

这样,在 storyboard 中只就不需要指定 StateLabel 的宽高了,它会根据内容变化,如果不想看到 IB 报错,可以设置 Intrinsic Size 为 Placeholder:

4. 使用CGAffineTransform(translationX: , y: 0) 方法时注意

x 和 y 是要移动的坐标。同时,这个坐标是以 View 处于 CGAffineTransform.identity 时的中心作为坐标原点的。原点左边为负,右边为正,原点上边为负,下边为正。93+85如果移到原位置就用 0,0 坐标,如果要移动到原点偏左 100 像素的位置,请用 -100,0 坐标:

let t2 = CGAffineTransform(translationX: -100, y: 0)

5. 关联类型如何使用范型约束?

associatedtype StateEnum: Equatable

associatedtype ChildScreen: Screen where ChildScreen.ItemType == ItemType

6. Extension 实现存储属性

    private static var _tempLayerKey: UInt8 = 0
    private var tempLayer: CALayer? {
        get { objc_getAssociatedObject(self, &UIImageView._tempLayerKey) as? CALayer }
        set { objc_setAssociatedObject(self, &UIImageView._tempLayerKey, newValue, .OBJC_ASSOCIATION_RETAIN) }
    }

7. 如何对一个 struct 类型数组进行累加?

使用 Sequence 扩展:

extension Sequence where Element == CGAffineTransform {
    func sum() -> Element { return reduce(CGAffineTransform.identity, { $0.concatenating($1)}) }
}

…


let t1 = CGAffineTransform(scaleX: ...)
let t2 = CGAffineTransform(translationX: ...)
shampooBottle.transform = [t1, t2].sum()

注意,范型约束使用 == 号而不是 冒号,因为 CGAffineTransform 是一个类,而不是协议。

8. 通过缩放 view 实现的屏幕适配方案

参考 22.

9. 比较对象地址

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

ObjectIdentifier(Person(name: “James”)).uintValue)

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

10. 协议扩展

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 = {

}()

11. 协议和关联类型

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

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

相当于(伪代码):
protocol Screen {
var items: [T] { get set }
}

这样,实现类可以通过 ItemType 来指定关联类型的具体类型,比如:
class MainScreen: Screen { typealias ItemType = String var items = String
}
其中对 typealias ItemType 赋值时只能用具体的类,而不能是 protocol。
此外, typealias ItemType = String 可以省略,因为编译器会自动推断关联类型。

12. 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))

13. Dynamic font

accountDescriptionLabel.adjustsFontForContentSizeCategory = true 然后将控件的 font 全部设置为动态字体:
● 在 Global
countryLabel.font = layout.font(for: .caption, allowDynamicFont: true)
● 非 Global
textField.font = .preferredFont(forTextStyle: .body)

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

● 对于 ViewController 里面的 UI 组件
你需要在 reloadContent/updateStyle 方法中设置组件的动态字体属性;因为在 BaseViewController 里面会监听 UIContentSizeCategory.didChangeNotification 通知,收到通知后会自动设置 setNeedReloadContent(), 而后者实际上会调用 reloadContent.
● 对于自定义 View 里面的 UI 组件
你需要在自定义 View 的 updateStyle 方法中设置 UI 组件的动态字体属性。

15. map 遍历时提供 index

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

在快速迭代中也可以提供索引:

fraganceButtons.enumerated().forEach { (index, btn) in

}

16. 关于 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()
}

17. 允许用户在 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 }
}

以上是关于Swift Tips(1-17)的主要内容,如果未能解决你的问题,请参考以下文章

swift常用代码片段

swift 代码片段

如何将这个 Objective-C 代码片段写入 Swift?

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

Swift Tips(18-32)

Swift Tips(18-32)