20190413のSwiftに関する記事は10件です。

初心者向け、iPhoneアプリをリリースするために参考にしたサイト

0 はじめに

プログラム初心者が、swiftを初めてさわって、AppleStoreでリリースするまでに、参考にしたサイトです。これから初めて、iPhoneアプリを作ってみようと思っている人の参考になればと思い、投稿しました。

1 Progate,ドットインストール

まずは、この二つのswiftのコースを学習しました。とにかく、この二つは最初に、取り掛かりやすく、やって損はないと思います。

Progate
ドットインストール

2 Udemy

動画学習サイトです。私はUdemyをメインに学習していました。セール期間中でなくても、2つ買うと2400円というお買い得情報が、各教材のページの下の方にあるので、2つ買って安く買うことができました。私が使ったのは主にこの3つです。

【6日で速習】iOS 11 Swift 4アプリ開発入門決定版 20個のアプリを作る(ARKit,CoreML,NFC)

こちらは、初心者にもわかりやすく、質問しても丁寧に答えてもらえます。

【最新版iOS12対応】プログラミング初心者が学ぶiPhoneアプリ開発入門

動画の説明の声が聞き取りやすく、いろんな種類のアプリが作成できるので、参考になります。

iOS 12 & Swift - The Complete iOS App Development Bootcamp

英語の教材なのですが、教材としての完成度が非常に高く、この値段で買えるのが不思議なくらいです。英語ですが、コードを書いて、動かす部分は動画を追って行けばできるので、十分参考になると思います。

3 わからない所

アプリを作る上で、検索している時間が1番長かったかなと思います。エラーメッセージをそのまま貼り付けてGoogle検索することで、かなり解決しました。学習を進めていくうちに、英語なので避けていたAppleのドキュメントを見ることが重要なことに気づいて、参考にするようにしました。

Apple Developer Documentation

4 書籍

ネットの学習サイトのみで作っていたのですが、紙ベースの本も欲しくなり購入しました。

絶対に挫折しないiPhoneアプリ開発「超」入門 第7版 【Xcode 10 & iOS 12】 完全対応 (Informatics&IDEA)

こちらは入門編です。とても丁寧でわかりやすいです。本屋さんでみやすい好みのものを選びました。

[改訂新版]Swift実践入門 ── 直感的な文法と安全性を兼ね備えた言語 (WEB+DB PRESS plus)

中級者以上向けです。辞書的に分からない時に調べる感じです。

5 まとめ

私は、Progate,ドットインストール1周した後、Udemy,ネット検索しながら自分の作りたいアプリを作りつつ、たまに書籍も見るという感じでした。これから学習を始める方の参考になれば幸いです。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AppDelegateから一番上のUIViewControllerにアクセスする方法

AppDelegateからUI操作を行う時、UIViewControllerが複数重なっている時は一番上(最前面)のUIViewControllerにアクセスする必要がありますよね。
今回はその方法について調べてみました。

準備

以下の様な3つのUIViewControllerをSegueで順に起動するサンプルプログラムを作成します。
AppDelegate.png
青いUIViewControllerにはFirstViewController、黄色いUIViewControllerにはSecondViewController、赤いUIViewControllerにはThirdViewControllerというクラス名をつけておきます。

処理

今回はAppDelegateのapplicationWillEnterForegroundに処理を記述しました。
ホームボタンやホームインジケータを押下してアプリを一度バックグラウンドにして、その後フォアグラウンドに復帰させた時に走るイベントです。

AppDelegate.swift
import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

-------------------- (中略) --------------------
    func applicationWillEnterForeground(_ application: UIApplication) {
        //一番手前のViewControllerを取得
        var viewController = UIApplication.shared.keyWindow?.rootViewController

        //PresentedViewControllerが取得できなくなるまでループ
        while viewController!.presentedViewController != nil {

            let presentedViewController = viewController!.presentedViewController

            //UIAlertControllerは対象外とする
            if let alertController = presentedViewController as? UIAlertController {
                //一番上がUIAlertControllerであればUIAlertControllerを閉じる
                alertController.dismiss(animated: true, completion: nil)
                break
            } else {
                //UIAlertControllerでなければ取得
                viewController = presentedViewController
            }
        }

        var message: String = ""

        if viewController is FirstViewController {
            message = "1番目のViewControllerです"
        }

        if viewController is SecondViewController {
            message = "2番目のViewControllerです"
        }

        if viewController is ThirdViewController {
            message = "3番目のViewControllerです"
        }

        let alertController: UIAlertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
        let defaultAction: UIAlertAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alertController.addAction(defaultAction)
        viewController!.present(alertController, animated: true, completion: nil)
    }
}

一番上のUIViewControllerを取得して、そこからアラートを表示する処理を記述しています。

サンプルプログラムを動かすとフォアグラウンドになった時に現在表示している画面をアラートで表示します。
ezgif-4-4554282cb2d2.gif

解説

まずUIApplicationからrootViewController(一番手前のUIViewController)を取得します。

var viewController = UIApplication.shared.keyWindow?.rootViewController

そしてrootViewControllerからpresentedViewControllerをループして取得します。

while viewController!.presentedViewController != nil {
    let presentedViewController = viewController!.presentedViewController

一番最後に返ってきたpresentedViewControllerが一番上のUIViewControllerになるのですが、ここで1点注意が必要です。
アラートを表示するUIAlertControllerや、App Extensionsを表示するUIActivityViewControllerもUIViewControllerを継承しているので、presentedViewControllerで返ってきます。
その為、一番上がUIAlertControllerやUIActiveViewControllerであった場合はその一つ手前のUIViewControllerが一番上のUIViewControllerになります。
今回のサンプルでもUIAlertControllerが表示されている可能性はあるので、UIAlertControllerだったらそれを閉じて、その一つ手前のUIViewControllerを一番上のUIViewControllerとして取得するようにしています。

while viewController!.presentedViewController != nil {

    let presentedViewController = viewController!.presentedViewController

    //UIAlertControllerは対象外とする
    if let alertController = presentedViewController as? UIAlertController {
       //一番上がUIAlertControllerであればUIAlertControllerを閉じる
       alertController.dismiss(animated: true, completion: nil)
       break
    } else {
       //UIAlertControllerでなければ取得
       viewController = presentedViewController
    }
}

どのUIViewControllerが一番上に来ているのかは「is」を使用してクラス名と型比較をすればわかります。

var message: String = ""

if viewController is FirstViewController {
   message = "1番目のViewControllerです"
}

if viewController is SecondViewController {
   message = "2番目のViewControllerです"
}

if viewController is ThirdViewController {
   message = "3番目のViewControllerです"
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

NSAttributedStringで下線をつける(Swift4)

NSAttributedStringとは

NSAttributedStringではフォントの大きさを変えたり色を変更することができます。
今回はそれを使ってbuttonに下線を付けて見ます。

今回は下記画像のような実装をbuttonに装飾したかったのでやり方を載せておきます。

IMG_3635.jpg

下線を表示させる

NSMutableAttributedStringを使うと部分的に文字の装飾などができます。
その中でNSUnderlineStyleを指定して表示させます。
ただそのまま使ってしまうと全てに下線が装飾されてしまうので表示エリアも指定します。

let underLineString = "(\(review))件"
let underLineString = "(\(review))件"
let attributeString = NSMutableAttributedString(
string: underLineString
)

attributeString.addAttributes([
//下線を表示
.underlineStyle: NSUnderlineStyle.single.rawValue],
//下線を表示する場所をattributeStringの二番目と後ろから三番目まで表示を指定
range: NSRange(location: 1, length: attributeString.length - 2)
)

完成コード

//reviewは何かしらの件数の数
let underLineString = "(\(review))件"
let attributeString = NSMutableAttributedString(
string: underLineString
)

attributeString.addAttributes([
//文字の大きさ・フォントの指定
.font: UIFont.systemFont(ofSize: 13),
//文字の色
.foregroundColor: UIColor.red,
//下線を表示
.underlineStyle: NSUnderlineStyle.single.rawValue],
//下線を表示する場所をattributeStringの二番目と後ろから三番目まで表示を指定
range: NSRange(location: 1, length: attributeString.length - 2)
)
//ここでbuttonに装飾を反映
ReviewButton.setAttributedTitle(attributeString, for: UIControl.State())
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UserDefaultsでStructを扱う

Swift 4 から Codable が使えるようになったので活用しましょう。

UserDefaultsstruct は保存できませんが String は保存できるので、
Codable を適用することで、
struct から JSON(String) に変換(エンコード)して保存
JSON(String) から struct に変換(デコード)して取得
というイメージです。

コード

扱う Struct

Codable を適用します。

struct Person: Codable {
    let name: String
    let age: Int
}

用意した Extension

直で書いてもいいですが、少し便利になるよう用意しました。
try? とかで適当に書いちゃってるのでエラーハンドリングなどどうぞ。

extension Encodable {

    var json: Data? {
        let encoder = JSONEncoder()
        return try? encoder.encode(self)
    }
}

extension Decodable {

    static func decode(json data: Data?) -> Self? {
        guard let data = data else { return nil }
        let decoder = JSONDecoder()
        return try? decoder.decode(Self.self, from: data)
    }
}

extension UserDefaults {

    func set(_ value: Codable?, forKey key: String) {
        guard let json = value?.json else { return }
        self.set(json, forKey: key)
        synchronize()
    }

    func codable<T: Codable>(forKey key: String) -> T? {
        let data = self.data(forKey: key)
        let object = T.decode(json: data)
        return object
    }
}

UserDefaults に保存 / から取得

let persons = [Person(name: "Sato Taro", age: 20), Person(name: "Ito Hanako", age: 10)]

// 保存
UserDefaults.standard.set(persons, forKey: "person")

// 取得
let restoredPersons: [Person]? = UserDefaults.standard.codable(forKey: "person")

// 出力  
print(restoredPersons!)
// [TempProject.Person(name: "Sato Taro", age: 20), TempProject.Person(name: "Ito Hanako", age: 10)]

環境

  • Swift 5
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift5のError型のちょっと便利になった点について

Swift5でResult型が公式にサポートされました:tada:

https://developer.apple.com/documentation/xcode_release_notes/xcode_10_2_release_notes/swift_5_release_notes_for_xcode_10_2

処理の結果をSuccess, FailureとしてパターンマッチングできるEnumです。

定義

enum Result<Success, Failure> where Failure : Error

使い方

switch result {
case .success(let someValue):
  // someValueを使った処理
case .failure(let error):
    print(error.message)
}

今までライブラリや似たような自作のEnumで同じことをやっていたので、何が嬉しいのか?と思ったのですが、リリースノートにこんな一文がありました。

D2jktT1U0AIQZE-.jpg

As part of this addition, the Error protocol now conforms to itself, which makes working with errors in a generic context easier. (SE-0235) (21200405)

Errorがそれ自身に準拠した型としてサポートされた:tada:

いままで

今まではErrorはただの存在型としてのProtocolであるため、それ自身を型として利用できずコンパイルエラーになります。
そのためただログ出力したり、Crashlyticsに通知するために扱うだけでもErrorに準拠した型を別途用意せねばならず面倒でした

(Result<Bool, Error>) -> Void
// Using 'Error' as a concrete type conforming to protocol 'Error' is not supported

これから

Resultが公式サポートされた一環として、Error型がそのまま使えるので地味に便利になりました。

どうやって実現しているのか

言語レベルの特別措置として実現しているようです

PR
https://github.com/apple/swift/pull/21073/commits/7fea4b845b1de824d675372df2341472f4e41c34

合わせて読みたい
Swiftのprotocolの存在型がそれ自身のprotocolに準拠しない理由

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】ABI StabilityとModule StabilityとLibrary Evolution Support

2019年3月18日に
Swift5.1のfinal branchingがアナウンスされてから
1ヶ月ほど経過しました。

リリース日はまだ出ていませんが
Appleのブログを見ている限り短いスパンでのリリースを考えており
次のバージョンについても学び始める時期なのかと思っています。
(WWDCに合わせるのかもしれませんが)

https://swift.org/blog/5-1-release-process/

Swift5.0ではABI Stabilityが達成されましたが、
Swift5.1はModule Stabilityを主な目的としています。

今回はSwift5.0で実現したABI Stability
そして5.1で達成されるModule Stability
さらに今後実現される予定のLibrary Evolution Support
について簡単にまとめてみたいと思います。

もし何か間違いなどごさいましたら教えていただけますとうれしいです:bow_tone1:

API Stability

API StabilityはSwiftのバイナリの互換性を確保することによって
Swiftのバージョンが異なっても
他のバージョンのstandard libraryやruntime libraryでアプリの実行が可能になります。

メリットとして下記のようなものがあります。

アプリサイズの削減

今まではアプリを使用する時に同じバージョンのstandard libraryやruntime libraryが必要になるため
これらをアプリのバンドルに含めなければならず容量が増えてしまっていました。(約5MB)

今後はObjective-Cのruntimeと同様にOSと一緒に同梱されるため
アプリのバンドルサイズを削減することができます。

パフォーマンスの向上

ホストのOSとより統合することができるため

  • 起動時間の短縮
  • runtime時の処理の最適化
  • 使用メモリの削減

が期待できます。

Appleが将来のOSバージョンでSwiftを使ったPlatformのFrameworkを提供可能になる

ブログに書かれていたのですが、具体的にどういうメリットがあるのかがわかっていません。

FrameworkがSwiftで書かれることで
Swiftで書かれたアプリとの連携が向上してパフォーマンスが良くなるということでしょうか:thinking:


ABI Stabilityは
2019年4月現在 Appleプラットフォームで実現していますが
LinuxやWindowsはまだのようです。

https://swift.org/blog/abi-stability-and-more/
https://swift.org/blog/abi-stability-and-apple/

Module Stability

ABI Stabilityは実行時のSwiftのバージョン差異を吸収してくれる一方、
Module Stabilityはコンパイル時のSwiftのバージョンの違いを吸収してくれます。

Swiftでは「swiftmodule」というアーカイブフォーマーットでライブラリのinterfaceを定義していますが、
このファイルの内容が現在のコンパイラのバージョン(実質Swiftのバージョン)と密接に結びついているために
別のバージョンでビルドされたライブラリをimportとしようとしてもコンパイラがエラーにしてしまいます。

これによってバイナリフレームワークを提供しているライブラリで、
バージョンに合わせてビルドされたライブラリを用意する必要が毎回あり
クライアント側でも
ライブラリが対応していないからSwiftのバージョンを上げることができない
ということが起きていました。

これをModule Stabilityが実現されると
どのコンパイラのバージョンで実装されたかを気にする必要がなくなります。

※ 実はObjective-CでWrapすればできるということをInstabugの方がブログで書かれていました。
https://instabug.com/blog/swift-5-module-stability-workaround-for-binary-frameworks/

詳細は下記のSwift Forumの中で話されています。
https://forums.swift.org/t/plan-for-module-stability/14551

Library Evolution Support

これはまだ最終決定がされていないものですが
AppleのブログではすでにStandard Libraryなどで実装されているようです。

Swift4.2で導入された@inlinableもこれの一つです。
https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md

今まではSwift(コンパイラ)のバージョンが上がった時の話をしていましたが、
今度はアプリで使用しているライブラリのバージョンが上がった時の話です。

現在ですと
ライブラリのバージョンが変わるたびに
アプリの再コンパイルが必要になります。

ブログによると
この動きにはメリットがあり
コンパイラがどのバージョンを使用しているのかがわかるので
最適化ができると書いてあります。
しかし
ライブラリのバージョンが違うと
この最適化が正しい最適化ではなくなる可能性があります。
そのため再コンパイルが必要になってくるようです。

Library Evolution Supportを達成すると
この再コンパイルが不要になります。

例えば
あるライブラリの更新が
他のライブラリの更新を引き起こすことがなくなります。

AとBのライブラリのbinary frameworkを使用している。

AはBのライブラリに依存している。

Bのライブラリを上げる

Aも再コンパイルしなければならなくなる

まとめ

Swiftは表だけではなく
裏側の仕組みもどんどん進化していっています。

普段はあまり目に見えないことに関しても
気がつかないところで恩恵を受けていたり
逆に何か影響を受けているかもしれないので
こういう裏側に仕組みを学ぶことも大事だなと思っています。

これからもSwiftがどう進化していくのかを追っていくのが楽しみです:smiley:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Swift】Swift5.0のABI StabilityとSwift5.1のModule Stabilityと その先のLibrary Evolution Support

2019年3月18日に
Swift5.1のfinal branchingがアナウンスされてから
1ヶ月ほど経過しました。

リリース日はまだ出ていませんが
Appleのブログを見ている限り短いスパンでのリリースを考えており
次のバージョンについても学び始める時期なのかと思っています。
(WWDCに合わせるのかもしれませんが)

https://swift.org/blog/5-1-release-process/

Swift5.0ではABI Stabilityが達成されましたが、
Swift5.1はModule Stabilityを主な目的としています。

今回はSwift5.0で実現したABI Stability
そして5.1で達成されるModule Stability
さらに今後実現される予定のLibrary Evolution Support
について簡単にまとめてみたいと思います。

もし何か間違いなどごさいましたら教えていただけますとうれしいです:bow_tone1:

API Stability

API StabilityはSwiftのバイナリの互換性を確保することによって
Swiftのバージョンが異なっても
他のバージョンのstandard libraryやruntime libraryでアプリの実行が可能になります。

メリットとして下記のようなものがあります。

アプリサイズの削減

今まではアプリを使用する時に同じバージョンのstandard libraryやruntime libraryが必要になるため
これらをアプリのバンドルに含めなければならず容量が増えてしまっていました。(約5MB)

今後はObjective-Cのruntimeと同様にOSと一緒に同梱されるため
アプリのバンドルサイズを削減することができます。

パフォーマンスの向上

ホストのOSとより統合することができるため

  • 起動時間の短縮
  • runtime時の処理の最適化
  • 使用メモリの削減

が期待できます。

Appleが将来のOSバージョンでSwiftを使ったPlatformのFrameworkを提供可能になる

ブログに書かれていたのですが、具体的にどういうメリットがあるのかがわかっていません。

FrameworkがSwiftで書かれることで
Swiftで書かれたアプリとの連携が向上してパフォーマンスが良くなるということでしょうか:thinking:


ABI Stabilityは
2019年4月現在 Appleプラットフォームで実現していますが
LinuxやWindowsはまだのようです。

https://swift.org/blog/abi-stability-and-more/
https://swift.org/blog/abi-stability-and-apple/

Module Stability

ABI Stabilityは実行時のSwiftのバージョン差異を吸収してくれる一方、
Module Stabilityはコンパイル時のSwiftのバージョンの違いを吸収してくれます。

Swiftでは「swiftmodule」というアーカイブフォーマーットでライブラリのinterfaceを定義していますが、
このファイルの内容が現在のコンパイラのバージョン(実質Swiftのバージョン)と密接に結びついているために
別のバージョンでビルドされたライブラリをimportとしようとしてもコンパイラがエラーにしてしまいます。

これによってバイナリフレームワークを提供しているライブラリで、
バージョンに合わせてビルドされたライブラリを用意する必要が毎回あり
クライアント側でも
ライブラリが対応していないからSwiftのバージョンを上げることができない
ということが起きていました。

これをModule Stabilityが実現されると
どのコンパイラのバージョンで実装されたかを気にする必要がなくなります。

※ 実はObjective-CでWrapすればできるということをInstabugの方がブログで書かれていました。
https://instabug.com/blog/swift-5-module-stability-workaround-for-binary-frameworks/

詳細は下記のSwift Forumの中で話されています。
https://forums.swift.org/t/plan-for-module-stability/14551

Library Evolution Support

これはまだ最終決定がされていないものですが
AppleのブログではすでにStandard Libraryなどで実装されているようです。

Swift4.2で導入された@inlinableもこれの一つです。
https://github.com/apple/swift-evolution/blob/master/proposals/0193-cross-module-inlining-and-specialization.md

今まではSwift(コンパイラ)のバージョンが上がった時の話をしていましたが、
今度はアプリで使用しているライブラリのバージョンが上がった時の話です。

現在ですと
ライブラリのバージョンが変わるたびに
アプリの再コンパイルが必要になります。

ブログによると
この動きにはメリットがあり
コンパイラがどのバージョンを使用しているのかがわかるので
最適化ができると書いてあります。
しかし
ライブラリのバージョンが違うと
この最適化が正しい最適化ではなくなる可能性があります。
そのため再コンパイルが必要になってくるようです。

Library Evolution Supportを達成すると
この再コンパイルが不要になります。

例えば
あるライブラリの更新が
他のライブラリの更新を引き起こすことがなくなります。

AとBのライブラリのbinary frameworkを使用している。

AはBのライブラリに依存している。

Bのライブラリを上げる

Aも再コンパイルしなければならなくなる

まとめ

Swiftは表だけではなく
裏側の仕組みもどんどん進化していっています。

普段はあまり目に見えないことに関しても
気がつかないところで恩恵を受けていたり
逆に何か影響を受けているかもしれないので
こういう裏側に仕組みを学ぶことも大事だなと思っています。

これからもSwiftがどう進化していくのかを追っていくのが楽しみです:smiley:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

アプリをそれっぽく見せるNavigationBarとTabBarの色の変え方 Swift初心者向け

完成品

tab_qiita.gif

対象

・iOSアプリの開発初心者で、それっぽいアプリ作りたいなと思っている方。
・Navigation Bar をカスタムしたいなと思っている方。
・Tab Bar をカスタムしたいなと思っている方。
・毎回 UIColor 使って入力するの面倒だなと思っている方。

開発環境

Xcode10.2
Swift5

概要

  1. Navigation Bar と Tab Bar の使い方がわかる!(Controller も)
  2. 色を一元管理することで、色の指定、色の変更を簡単にする!
  3. Navigation Bar と Tab Bar の色を統一して、アプリっぽさを出す!

セットアップ

コードを書く前に、Xcode を触っていきます!

プロジェクトの立ち上げ

スクリーンショット 2019-04-12 12.58.40setup.png

画面の準備

1. 初期の View Controller の削除

Main.storyboard にある ViewController は使わないので、消し去りましょう。
スクリーンショット 2019-04-12 13.03.37remove.png

2. Tab Bar Controller を入れる

次に Tab Bar Controller を Storyboard に挿入します。
1. Library をクリック
2. 検索欄に tabbar と入力
3. 結果に出てきた、Tab Bar Controller をドラッグ&ドロップで召喚

スクリーンショット 2019-04-12 13.04.04addtab.png
4. OK!
スクリーンショット 2019-04-12 13.04.35addedTabbar.png

Tab Bar Controller をちょっとだけ編集

アイコンがデフォルトだと味気ないので、変更します!
1. Tab Barを選択
2. System Item から好きなものを選択
3. OK!
(もう1つの画面も同様に編集)
スクリーンショット 2019-04-12 13.24.15.jpg

3. Navigation Controller を入れる

  1. Tab Bar Controller の次の画面の上にある黄色いところを選択
  2. Editor を選択
  3. Embed In → Navigation Controller を選択 スクリーンショット 2019-04-12 13.05.14addEmbedIn_navigationbar.jpg
  4. 完成!(もう1つの画面も同様に編集) スクリーンショット 2019-04-12 13.05.28.png

Navigation Bar をちょっとだけ編集

タイトルを入れましょー!
1. Navigation Barをクリック
2. Titleを編集
3. OK!
(もう1つの画面も同様に編集)

スクリーンショット 2019-04-12 13.08.03.jpg

Is initial View Controllerの設定

最初に View Controllerを消してしまったので、 Is initial View Controllerの設定がなくなってしまいました。
このまま、プロジェクトをビルドしてRunすると、どの ViewController を表示すれば良いのかがわからず、下記のエラーが発生してしまうのでここで設定しておきます。

Failed to instantiate the default view controller for UIMainStoryboardFile 'Main' - perhaps the designated entry point is not set?
  1. 黄色のところを選択
  2. Is initial View Controller を選択
  3. 矢印があればOK! スクリーンショット 2019-04-12 22.28.51.jpg

実装

UIColor を継承したクラス作成

なぜ UIColor を継承したクラスを作成するかというと、1つのファイルで色の指定を管理したいからです。
Tab Bar などのアイテムに色をつける際に、1つ1つの画面で色を設定するのは面倒です。
例えば、全ての画面で Tab Bar の色を黒(#000000)にしていたとします。
色の変更したいな〜と思って、白(#FFFFFF)にすることになったとしたら、全ての画面に適用されているソースコードを変更しなければなりません。
10画面あったら、10個もコードを変更する手間がかかります。

色の指定を1つのクラスにし、そのクラスのプロパティを参照することで、1つのクラスで色を管理できるようにでき、修正が簡単になり、保守性が上がります!

また、UIColor を継承しているクラスを作成することによって、「プロジェクトで利用する色なんだ」とクラスを見ただけで、他の人(共同開発したり、保守運用をしている人)に意図が伝わるというメリットもあります。

  1. Ctrl + N でファイル作成 (Swift File)
  2. SoreppoiAppColor.swift ファイル選択(ファイル名は適当に。プロジェクト名+Color だと分かりやすいかも)
  3. 以下のコードを記述
SoreppoiAppColor.swift
import Foundation
import UIKit

class SoreppoiAppColor {
    // 濃い緑を返す
    class var primary: UIColor {
        return rgbColor(rgbValue: 0x73C6B6)
    }

    // 薄いオレンジを返す
    class var secondary: UIColor{
        return rgbColor(rgbValue: 0xFFD6BA)
    }

    // 白っぽい灰色を返す
    class var background: UIColor{
        return rgbColor(rgbValue: 0xFAF9F9)
    }

    // #FFFFFFのように色を指定できるようにするメソッド!色が使いやすくなる。
    // ここでしか使わないので privateメソッドにする。
    private class func rgbColor(rgbValue: UInt) -> UIColor{
        return UIColor(
            red:   CGFloat((rgbValue & 0xFF0000) >> 16) / 255.0,
            green: CGFloat((rgbValue & 0x00FF00) >>  8) / 255.0,
            blue:  CGFloat( rgbValue & 0x0000FF)        / 255.0,
            alpha: CGFloat(1.0)
        )
    }
}

Tab Bar Controller ファイルを作成

目的:Tab Bar の色を変更する

  1. Ctrl + N でファイル作成 (Cocoa Touch Class)
  2. 「Subclass of: 」 で UITabBarController を選択して作成 (Class名は適当に)
    スクリーンショット 2019-04-12 13.10.48.png

  3. 以下のコードを記述。コードの説明は、コメントにて。
    (追記部分以外は最初から記述してあります。)

SoreppoiAppTabBarController.swift
import UIKit

class SoreppoiTabBarController: UITabBarController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // -----**追記部分**----- //
        // アイコンの色を変更できます!
        UITabBar.appearance().tintColor = SoreppoiAppColor.secondary
        // 背景色を変更できます!
        UITabBar.appearance().barTintColor = SoreppoiAppColor.primary
        // -----**追記部分**----- //
    }

}

4_ Tab Bar Controller に、swift ファイルを適用
このままでは Tab Bar Controller と、SorreppoiAppTabBarController.swift ファイルが別々のもの(連携していない)になっているので、swift ファイルで Tab Bar Controller で制御できるようにしましょう!

Main.storyboard を開いて、Tab Bar Controller に swift ファイルを適用

スクリーンショット 2019-04-12 13.11.54.jpg

Navigation Bar の色を変更

AppDelegate.swift のコードを修正

なぜ function にしているかというと、function名を使ってどのような処理をしているかが、直感的にわかるからです!
もちろん function化せずに処理を書いても良いのですが、パッと見ただけですぐに処理内容がわからず、処理を読み解く時間が発生します。
function化することで、誤った解釈を防ぐことができ、可読性も向上します!

追記部分だけ記述してください!

AppDelegate.swift
import UIKit
import CoreData

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        // -----**追記部分**----- //
        changeNavigationBarColor()
        // -----**追記部分**----- //
        return true
    }
    // -----**追記部分**----- //
    func changeNavigationBarColor() {
        // 全てのNavigation Barの色を変更する
        // Navigation Bar の背景色の変更
        UINavigationBar.appearance().barTintColor = SoreppoiAppColor.primary
        // Navigation Bar の文字色の変更
        UINavigationBar.appearance().tintColor = SoreppoiAppColor.secondary
        // Navigation Bar のタイトルの文字色の変更
        UINavigationBar.appearance().titleTextAttributes = [NSAttributedString.Key.foregroundColor: SoreppoiAppColor.background]
    }
    // -----**追記部分**----- //


///ここから先は省略///

完成したそれっぽいアプリ(再度掲載)

tab_qiita.gif

まとめ

Navigation Bar と Tab Bar の色を統一してみるだけでなんかそれっぽいアプリに見えませんか!?(自分だけかな。)
この画面をベースにアプリ開発をしていくと少しだけテンション上がります。それっぽく見えるかもなんで!
Navigation Controller を使っているので、次の画面に遷移した時に、上に 「<戻る」 も付くので便利ですよ!

長文でしたがお読みいただきありがとうございました。

この投稿が誰かの手助けとなると、とても嬉しいです!
ぜひコメントください。

間違っている点、もっとこうした方がいいよ、という点などご指摘お待ちしております!
よろしくお願いいたします。

参考にさせていただいた記事

@senseiswift さん
ExtensionでUIColorにブランドカラーを返す拡張メソッド追加

コードログ
Swift – iOS:ナビゲーションバーの色を変更する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift Package Manager Commands

Init package(Executable)

$ swift package init --type executable

Init package(Library)

$ swift package init --type library

Build package

$ swift build

Test package

$ swift test

Run package

$ swift run

Define dependent package

Local

.package(path: "../Module"),

Branch

.package(url: "https://github.com/<USER>/<REPO>.git", .branch("master")),
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Swift文法まとめ

エラー処理の使い分け

こちらも参考に↓
https://qiita.com/koishi/items/67cf4d0f51c4d79f1d22

Optional型

エラーの詳細情報が不要で、結果の成否のみによってエラーを扱える場合。

Result型

非同期処理の場合に用いる。型引数Errorと同じ型のエラーしか扱えない。使うメリットは、処理の結果が成功か失敗かのいずれかに絞られること。

do-catch文

同期処理の場合に用いる。do節内にエラーが発生する可能性のある処理を記述し、エラーが発生すると、catch節にプログラムの制御が移り、catch節内でエラーの詳細を受け取り、エラーの詳細に応じて処理を切り替えることができる。Errorプロトコルに準拠した型であればどのような型も扱うことができる。
throw文を用いると、エラーを発生させることができる。Errorプロトコルに準拠した値を使って「throw エラー値」のように書く。
Tryキーワードを用いると、エラーを発生させる可能性のある処理を実行することができる。
使うメリットは、上記と同様に、処理の結果が成功か失敗かのいずれかに絞られること。

fatalError(_:)関数

エラー発生時にプログラムの実行を終了したい場合。

アサーション

デバッグ時のみ、エラー発生時にプログラムの実行を終了したい場合。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む