チンチラのフンみたいなもの

毎日の学んだことを書いていきます

記念すべき初投稿

Notionにまとめていたノートをそのまま持ってきました。3日分くらいの内容です。AppDelegateとかの話が分からなすぎて調べまくったものの、数日たったらめちゃくちゃ忘れてて絶望したのでメモとることにしました。またNotionでまとめるだけだと自分にしか見えず、何もやってない人と思われるが嫌なのでブログで発信していくことにしました。草生やさなくても勉強はしてるんだぞと言う証明になれば。

これから毎日Notionでまとめたノートをコピペして記事にしようと思います。web上でチマチマ下書き記事を更新していくよりは、アプリでぱぱっとメモった方が楽だと思うのでそうします。Notionのいいところはコピペするとその時に使ったマークダウンがそのまま反映されるところです。なのでこの記事もめちゃくちゃ読みにくいと言うものにはなっていないと思います。まあそれでも修正とかはしていないので読みづらいとは思います。なのでそういう記事には「無修正」と言うちょっとエッチなタグをつけますので、その記事を読みたくない場合は「無修正」タグを避けて頂ければと思います。これからよろしくお願いします。

  • 中に配列を持たないarray型を一つの文字列にするjoinedメソッド(参考参考
let list = ["1", "2", "3"]
let string1 = list.joined(separator: "")
let string2 = list.joined(separator: "-")
//separatorで間に何を入れてくぎるかを指定できます。
print(string1) // "123"
print(string2) // "1-2-3
  • CustomStringConvertibleに準拠していると、インスタンス化するだけでprint文がdescriptionプロパティを参照するようになる(参考
struct Checkerboard {
  enum Square: String {
    case empty = "▪️"
    case red = "🔴"
    case white = "⚪️"
  }

  typealias Coordinate = (x: Int, y: Int)

  private var squares: [[Square]] = [
    [ .empty, .red,   .empty, .red,   .empty, .red,   .empty, .red   ],
    [ .red,   .empty, .red,   .empty, .red,   .empty, .red,   .empty ],
    [ .empty, .red,   .empty, .red,   .empty, .red,   .empty, .red   ],
    [ .empty, .empty, .empty, .empty, .empty, .empty, .empty, .empty ],
    [ .empty, .empty, .empty, .empty, .empty, .empty, .empty, .empty ],
    [ .white, .empty, .white, .empty, .white, .empty, .white, .empty ],
    [ .empty, .white, .empty, .white, .empty, .white, .empty, .white ],
    [ .white, .empty, .white, .empty, .white, .empty, .white, .empty ]
  ]
}

extension Checkerboard: CustomStringConvertible {
  var description: String {
    return squares.map { row in row.map { $0.rawValue }.joined(separator: "") }
        .joined(separator: "\n") + "\n"
  }
}
  • substructを使うと配列や辞書でなくても添字が使えるようになる。処理としてはコンピューテッドプロパティと同じだが、引数が使えることと、特定の場面で可読性が向上することが特徴(参考参考
subscript(coordinate: Coordinate) -> Square {
  get {
    return squares[coordinate.y][coordinate.x]
  }
  set {
    squares[coordinate.y][coordinate.x] = newValue
  }
}
  • RootViewControllerの入れ替え

SceneDelegate.swiftってなに? - Qiita

[Swift 5]RootViewControllerを適用する備忘録 - Qiita

iOSのrootViewControllerを置き換える - Qiita

  • addChild(_:)  現在のビューコントローラーに子ビューコントローラーを追加する。子ビューコントローラーが既にコンテナビューコントローラーである時、追加される前にそのコンテナビューから削除される。ここで言うコンテナビューとはコントローラービューをまとめて移動処理等を管理するコントローラーのことを指すと思われる。またaddChildを呼ぶさいselfがいらないのは呼ぶのが現在のビューコントローラーであるからだと思われる。
  • didMove(toParent:)  独自のコンテナービューで新しいコントローラービューへの移行が完了した場合、または移動しない場合にはこのメソッドを呼ぶ必要がある。removeFromParen()は子を削除した後に子ビューコントローラーのdidMove()メソッドを自動的に呼ぶ
  • willMove(toParent:)  独自のコンテナビューでremoveFromParentメソッドを呼び出す前にwillMove()メソッドを呼びだし、親の値にnilを返さなければならない。addChild()メソッドを呼び出すと子ビューコントローラーのwillMove()メソッドを自動できに呼ぶ。
  • removeFromSurperView()  ビューをそのスーパービューとウインドウから削除する。
  • removeFromParent()  親ビューコントローラから子ビューコントローラーを削除する。コンテナビューからのみ呼ばれる。
  • addSubview(_:)  viewのサブビューのリストの末尾に追加する。呼び出すのはあくまでview

LaunchScreenの表示時間の変更

【Swift】LaunchScreenの待機時間を設定する方法 - Qiita

チュートリアル画面ライブラリInstructionsの使い方

https://github.com/ephread/Instructions

1.コーチマークを表示させるコントローラーでCoachMarksControllerクラスのインスタンス化とプロトコルの準拠させる

class DefaultViewController: UIViewController,
                             CoachMarksControllerDataSource,
                             CoachMarksControllerDelegate {
    let coachMarksController = CoachMarksController()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.coachMarksController.dataSource = self
    }
}

2.CoachMarksControllerDataSourceプロトコルに準拠すると3つのメソッドを実装する必要がある。1つは表示するコーチマークの数を問うもの。

func numberOfCoachMarks(for coachMarksController: CoachMarksController) -> Int {
    return 1
}

2つ目はメタデータを要求するもの。コーチマークの位置や表示方法をカスタマイズできるが、見た目は変更できない。coachMarkAtはindexPathのようなもの

let pointOfInterest = UIView()

func coachMarksController(_ coachMarksController: CoachMarksController,
                          coachMarkAt index: Int) -> CoachMark {
    return coachMarksController.helper.makeCoachMark(for: pointOfInterest)
}

3つ目は2つのビュー(cellForRowAtIndexPathのようなもの)をタプルの形で提供する。bodyViewはコーチマークの核となるもので必須だが、arrowViewはオプション

func coachMarksController(
    _ coachMarksController: CoachMarksController,
    coachMarkViewsAt index: Int,
    madeFrom coachMark: CoachMark
) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) {
    let coachViews = coachMarksController.helper.makeDefaultCoachViews(
        withArrow: true,
        arrowOrientation: coachMark.arrowOrientation
    )

    coachViews.bodyView.hintLabel.text = "Hello! I'm a Coach Mark!"
    coachViews.bodyView.nextLabel.text = "Ok!"

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

3.dataSourseのセットアップが完了したら、コーチマークの表示を開始します。ほとんどの場合selfを返して開始。windowの子として自身のビューコントローラを追加し、そのビューコントローラーの子としてCoachMarksControllerを追加する。そうすることで、CoachMarksControllerはサイズ変更イベントを受け取ることができる。またその処理を正しく行うためviewDidLoadでstartメソッドを読んではいけない

override func viewDidAppear(animated: Bool) {
    super.viewDidAppear(animated)

    self.coachMarksController.start(in: .window(over: self))
}

4.ビューが消えたら必ずフローを停止させる必要がある。viewDidDisappearでstop(immediately: true)を呼び出すとビューが消えた時にフローが直ちに停止することが確認できる

override func viewWillDisappear(animated: Bool) {
    super.viewWillDisappear(animated)

    self.coachMarksController.stop(immediately: true)
}

オプション

  • オーバーレイの色変更
  • オーバーレイをタッチすると次のステップにいく
  • 切り出したビューやオーバーレイ全体でのタッチイベントを下のビューに転送できる
  • コーチマークの色、背景色、丸み
  • 矢印の向き
func coachMarksController(
    _ coachMarksController: CoachMarksController,
    coachMarkViewsAt index: Int,
    madeFrom coachMark: CoachMark
) -> (bodyView: UIView & CoachMarkBodyView, arrowView: (UIView & CoachMarkArrowView)?) {
    let coachViews = coachMarksController.helper.makeDefaultCoachViews(
        withArrow: true,
        arrowOrientation: coachMark.arrowOrientation
    )
}
  • 切り出し方の形
var coachMark = coachMarksController.helper.makeCoachMark(
    for: customView,
    cutoutPathMaker: { (frame: CGRect) -> UIBezierPath in
        // This will create an oval cutout a bit larger than the view.
        return UIBezierPath(ovalIn: frame.insetBy(dx: -4, dy: -4))
    }
)
  • どのwindowで表示させるか
  • コーチマークをスキップする手段を提供する。プロトコルに準拠してどのコントロールをタップする必要があるか知らせる
public protocol CoachMarkSkipView: AnyObject {
    var skipControl: UIControl? { get }
}
  • コーチマークが表示される時、消える時、全てのコーチマークが表示された時知らせる
func coachMarksController(
    _ coachMarksController: CoachMarksController,
    willShow coachMark: inout CoachMark,
    at index: Int
)
func coachMarksController(
    _ coachMarksController: CoachMarksController,
    willHide coachMark: CoachMark,
    at index: Int
)
func shouldHandleOverlayTap(
    in coachMarksController: CoachMarksController,
    at index: Int
) -> Bool
  • 一時停止中にオーバーレイを隠すか、オーバーレイを隠してタッチブロック機能だけ残すか