Swift Tips
Posted 颐和园
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Swift Tips相关的知识,希望对你有一定的参考价值。
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 实现存储属性
extension UIButton
private static var _spinner: UInt8 = 0
var spinner: UIActivityIndicatorView?
get
if let spinner = objc_getAssociatedObject(self, &UIButton._spinner) as? UIActivityIndicatorView
return spinner
else
var spinner: UIActivityIndicatorView!
if #available(ios 13.0, *)
spinner = UIActivityIndicatorView(style: .medium)
else
spinner = UIActivityIndicatorView(style: .white)
spinner.color = .white
spinner.translatesAutoresizingMaskIntoConstraints = false
addSubview(spinner)
NSLayoutConstraint.activate([
spinner.centerXAnchor.constraint(equalTo: centerXAnchor),
spinner.centerYAnchor.constraint(equalTo: centerYAnchor)
])
return spinner
set objc_setAssociatedObject(self, &UIButton._spinner, 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”))
通过比较 ObjectIdentifier 即可得知是否是同一对象。但它不能用于比较结构体。
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<T>
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
...
以上是关于Swift Tips的主要内容,如果未能解决你的问题,请参考以下文章