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