---
# System prepended metadata

title: Views features

---

# 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)
```

![](https://i.imgur.com/OuWaceP.png)


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
``` 

![](https://i.imgur.com/x7ymaru.png)

會跑到左上角是因為我們修改了 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)
```
![](https://i.imgur.com/ornXSLb.png)


## 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)
```
結果為：

![](https://i.imgur.com/WCOA46H.png)

即使 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 如下圖：

![](https://i.imgur.com/AEJjjCa.png)

透過切換裝置按鈕可以看出 plus 當橫置時他的 width 是 Ｒ（regular） ，height 是 Ｃ(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 如下圖：

![](https://i.imgur.com/XVbPR63.png)

```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 的左上邊

