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的主要内容,如果未能解决你的问题,请参考以下文章

iOS开发中OC和swift的对比

ios OC、swift混编制作framework

iOS-swift-基础篇1

iOS开发系列--Swift 3.0

swift 2016年 - iOS / PlayerViewController.swift

swift iOS URL Schemes Swift