【Swift】チュートリアルライブラリ使ってみた

# はじめに
チュートリアルを作成できるライブラリを使ってみたので基本的な使い方を紹介できたらと思います。

↓↓リポジトリはこちら↓↓

https://github.com/ephread/Instructions

# どんなライブラリ？
![Simulator Screen Recording - iPhone 12 - 2022-10-23 at 20.24.57.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/47bc24dd-03da-7b8a-cf8f-5c16bf1ac7c7.gif)

[こちら](https://apps.apple.com/jp/app/swipe-counter/id6443832677)のアプリで使用しているので是非インストールして実際に触ってみてください！

# 基本実装
```swift
import UIKit
import SnapKit
// Instructionsをインポート
import Instructions

// CoachMarksControllerDelegateとCoachMarksControllerDataSourceを準拠させる
class ViewController: UIViewController, CoachMarksControllerDelegate, CoachMarksControllerDataSource {

    // CoachMarksControllerを定義
    private let coachMarksController = CoachMarksController()

    private let button1: UIButton = {
        let button = UIButton(frame: .zero)
        button.setTitle("ボタン1", for: .normal)
        button.setTitleColor(.black, for: .normal)

        return button
    }()

    private let button2: UIButton = {
        let button = UIButton(frame: .zero)
        button.setTitle("ボタン2", for: .normal)
        button.setTitleColor(.black, for: .normal)

        return button
    }()

    private let button3: UIButton = {
        let button = UIButton(frame: .zero)
        button.setTitle("ボタン3", for: .normal)
        button.setTitleColor(.black, for: .normal)

        return button
    }()

    override func viewDidLoad() {
        super.viewDidLoad()
        setup()
    }

    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        // チュートリアルを開始
        coachMarksController.start(in: .window(over: self))
    }

    private func setup() {
        // delegateとdataSourceを設定
        coachMarksController.delegate = self
        coachMarksController.dataSource = self

        view.backgroundColor = .white

        view.addSubview(button1)
        view.addSubview(button2)
        view.addSubview(button3)

        button1.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalTo(button2.snp.top).offset(-50)
        }
        button2.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        button3.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.top.equalTo(button2.snp.bottom).offset(50)
        }
    }

    func numberOfCoachMarks(for coachMarksController: Instructions.CoachMarksController) -> Int {
        // チュートリアルの数
        return 3
    }

    func coachMarksController(_ coachMarksController: Instructions.CoachMarksController, coachMarkAt index: Int) -> Instructions.CoachMark {
        // チュートリアルでフォーカスするUIViewの配列
        let highlightViews: [UIView] = [button1, button2, button3]

        var coachMark = coachMarksController.helper.makeCoachMark(for: highlightViews[index])

        return coachMark
    }

    func coachMarksController(_ coachMarksController: Instructions.CoachMarksController, coachMarkViewsAt index: Int, madeFrom coachMark: Instructions.CoachMark) -> (bodyView: (UIView & Instructions.CoachMarkBodyView), arrowView: (UIView & Instructions.CoachMarkArrowView)?) {
        // チュートリアルで表示する文字の配列
        let highlightTexts: [String] = ["これはボタン1です", "これはボタン2です", "これはボタン3です"]

        let coachViews = coachMarksController.helper.makeDefaultCoachViews(
            withArrow: true,
            withNextText: false,
            arrowOrientation: coachMark.arrowOrientation
        )

        coachViews.bodyView.hintLabel.text = highlightTexts[index]

        return (bodyView: coachViews.bodyView, arrowView: coachViews.arrowView)
    }
}
```


# 解説
ライブラリをインポートします
```swift
import Instructions
```
`CoachMarksControllerDelegate`と`CoachMarksControllerDataSource`を準拠させます
```swift
class ViewController: UIViewController, CoachMarksControllerDelegate, CoachMarksControllerDataSource {
}
```
下記の3つのメソッドが必須実装になります
```swift
// チュートリアルの数を設定
func numberOfCoachMarks(for coachMarksController: Instructions.CoachMarksController) -> Int {

}

// コーチマークの位置や表示方法を設定
func coachMarksController(_ coachMarksController: Instructions.CoachMarksController, coachMarkAt index: Int) -> Instructions.CoachMark {

}

// コーチマークのUIを設定
func coachMarksController(_ coachMarksController: Instructions.CoachMarksController, coachMarkViewsAt index: Int, madeFrom coachMark: Instructions.CoachMark) -> (bodyView: (UIView & Instructions.CoachMarkBodyView), arrowView: (UIView & Instructions.CoachMarkArrowView)?) {

}
```
`CoachMarksController()`をインスタンス化します
```siwft
private let coachMarksController = CoachMarksController()
```
delegateとdataSourceを自身に設定します
```swift
coachMarksController.delegate = self
coachMarksController.dataSource = self
```

# もっとカスタマイズしたい
### チュートリアルのスキップボタンの実装
```swift
let skipButton = CoachMarkSkipDefaultView()
skipButton.isStyledByInstructions = false
skipButton.setTitle("スキップ", for: .normal)
skipButton.setTitleColor(.black, for: .normal)
skipButton.layer.cornerRadius = 10
skipButton.backgroundColor = .systemGray5

self.coachMarksController.skipView = skipButton
```

### 背景色を変更する
```swift
coachMarksController.overlay.backgroundColor = UIColor(red: 0.2, green: 0.2, blue: 0.2, alpha: 0.5)
```

### 画面のどこをタップしても次へ進む
```swift
coachMarksController.overlay.isUserInteractionEnabled = true
```
### コーチマークのwillShow
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, willShow coachMark: inout CoachMark, beforeChanging change: ConfigurationChange, at index: Int) {

}
```
### コーチマークのdidShow
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, didShow coachMark: CoachMark, afterChanging change: ConfigurationChange, at index: Int) {

}
```
### コーチマークのwillHide
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, willHide coachMark: CoachMark, at index: Int) {

}
```
### コーチマークのdidHide
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, didHide coachMark: CoachMark, at index: Int) {

}
```
### コーチマークをタップした時
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, didTapCoachMarkAt index: Int) {

}
```

### チュートリアルをスキップした時
```swift
func coachMarksController(_ coachMarksController: CoachMarksController, didEndShowingBySkipping skipped: Bool) {

}
```

# おわり
自分が触ってみた機能しか記載していません。
まだまだ機能があるので別の機会で新しい機能に触れたら随時追加していきます。