# Views features
## Bounds and Center
#### `inset(by:)` 在特定的 view 插入一個 view
```swift
let v1 = UIView(frame:CGRect(113, 111, 132, 194)) //1.
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10)) //2.
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
self.view.addSubview(v1)
v1.addSubview(v2)
```

1. 首先製作一個空的 view ,之後用來插入另一個 view
2. 製作 view v2 並在初始化時 `frame` 直接利用 `inset(by)` 傳入 v1 的 `bounds`, 如此一來 dx 和 dy 就是插入後的間距。
`inset(by)`,可以幫你做好需要插入一個置中 `superview` 的 `subview` 可以直接利用 `superview` 的 `bounds` 來當作 subview 的 frame。
v2 的 frame 遵從其 superview v1 的 bound,代表 v1 的 origin x, y 各自加 10 的話,v2 會跑到 v1 的左上角。
在上面的 code 上再加上這兩行:
```swift
v1.bounds.origin.x += 10
v1.bounds.origin.y += 10
```

會跑到左上角是因為我們修改了 v1 的 frame ,本來在 v2 的 frame 是靠在 v1 的 bounds (x:10, y:10) 的地方,但因為現在 v1 的 bounds 已經移動到 (x:10, y:10) 的地方,
所以修改後 v2 看起反而是往左上移動了。
- 如果需要置中一個 subview 你可以這樣做:
```swift
let v1 = UIView(frame:CGRect(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:CGRect(0, 0, 122, 184))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
self.view.addSubview(v1)
v1.addSubview(v2)
// 幫你置中 v2 如果是 v1 是 subview
v2.center = v1.convert(v1.center, from:v1.superview)
```

## Transform
一個 view 可以不只是矩形的,他可以變形,變形的依據透過一個 struct `CGAffineTransform` 用來表示一個 3X3 的矩陣,struct 包含 6 個變數,剩餘的 3 個值是常數不變的。
在初始化 `CGAffineTransform` 時,就提供了基本變形的選項,如: rotation, scaling, translation 旋轉,變形,移動。
如以下選轉 v1 的角度:
```swift
let v1 = UIView(frame:CGRect(113, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:v1.bounds.insetBy(dx: 10, dy: 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
self.view.addSubview(v1)
v1.addSubview(v2)
v1.transform = CGAffineTransform(rotationAngle: 45 * .pi/180)
print(v1.frame)
```
結果為:

即使 superview 旋轉了,bounds 也沒有被改變,所以 subview 的位置不會被改變。這件觀念告訴我們,如果預設的 transform 已經被改變,那麼 view 的 frame 不應該再去設定,因為它僅僅代表包圍 view 的範圍。
## UITraitCollection
`traitCollection` 包含了 iOS 裝置的各種特性參數,囊括了`horizontalSizeClass`,`verticalSizeClass`, `displayScale` (螢幕解析度), 及 `userInterfaceIdiom` (顯示 ipad 或 iPhone),在用`traitCollection` 的所有參數之前的類別需要採取 `UITraitEnvironment` protocol.
### Size Class
在 View 裡,我們關心的是 `UIUserInterfaceSizeClass` 他擁有這兩個參數 與`horizontalSizeClass`,`verticalSizeClass`, 而這兩個參數他用一個 enum 來代表 view 目前的兩種 case:
- .regular
- .compact
你可以透過 XCode 來研究各個裝置的 width 跟 height 的
size class 如下圖:

透過切換裝置按鈕可以看出 plus 當橫置時他的 width 是 R(regular) ,height 是 C(compact)跟其他的 iPhone 裝置是不太一樣的。
Size class 在 app 運行的期間,會因為使用者的行為改變而改變,因此便會重送這些訊息到 `traitCollectionDidChange(_:) ` delegate,舊的 trait collection 會被當作參數,而目前的 trait collection 可以經由 `self.traitCollection` 取得。
### Layout
#### Autoresizing
Autoresizing 就像是給 subview strut (支柱) 或者 spring (彈簧),strut 是固定的,spring 則是可以移動的,Autoresizing 可以是設定 view 的內部,或者是外部,以及水平或垂直。
- 假設有一個 subview 要建立在 superview 裡且置中,但他會依 superview 去做變形,所以我們會在 subview 裡設定 `.flexibleHeight`, `.flexibleWidth`, subview 外部設定 strut。
- 假設有一個 subview 要建立在 superview 裡且置中,但不會因 superview 變形而變形 ,這時 subview 裡面要設定 sturt。
- 假設有一個確認按鈕要建立在 superview 的右下角,這時 strut 要在 button 的外部右下邊設定, 要在外部的左上邊設定。
組合 autoresizeMask layout:
1. 首先產生三個 view 如下圖:

```swift
let v1 = UIView(frame:CGRect(100, 111, 132, 194))
v1.backgroundColor = UIColor(red: 1, green: 0.4, blue: 1, alpha: 1)
let v2 = UIView(frame:CGRect(0, 0, 132, 10))
v2.backgroundColor = UIColor(red: 0.5, green: 1, blue: 0, alpha: 1)
let v3 = UIView(frame:CGRect(
v1.bounds.width-20, v1.bounds.height-20, 20, 20))
v3.backgroundColor = UIColor(red: 1, green: 0, blue: 0, alpha: 1)
self.view.addSubview(v1)
v1.addSubview(v2)
v1.addSubview(v3)
```
上面的 code 中, v1 為綠色也是 v2, v3 的 superview,接下來我們要讓 v2 跟著 superview 延展,而 v3 不動。
透過以下兩條 layout 來固定:
```swift
v2.autoresizingMask = .flexibleWidth
v3.autoresizingMask = [.flexibleTopMargin, .flexibleLeftMargin]
```
v2 加入了延展邊緣的條件, v3 加入兩個 spring 延展至 superview 的左上邊