20210127のSwiftに関する記事は5件です。

?yawacom - ?Storyboardを使わず画面遷移を行う

2021/01/27現在編集中

Storyboardの削除

Storyboardはぱっと見わかりやすくて最高だとおもっていたのですが,画面が増えるにつれ重くなっていったり,いろいろなプロパティを直接Storyboardに記述することになるので後々修正が大変だったりします.なので今回はStoryboardを使わずコードとxibで作っていきます.

ということで
?Main.storyboard はいらないので削除
?General -> Deployment Info -> Main Interface のMainという文字列を消して空欄に
?Info.plistのApplication Scene Manifest -> Scene Configuration -> Application Session Role -> Item -> Storyboard Name を➕➖の➖を押して削除
を行います.

▽参考文献
【Swift4】アプリの初回起動画面を storyboard ではなく xib にする

アプリ初回起動

まずアプリを立ち上げた時にユーザ登録画面に行くのかログイン画面に行くのかを分岐させます.
最初に必ずAppDelegate/SceneDelegateが呼ばれるので,そこでまずRoutingViewControllerに移動します.
今回はターゲットをiOS13以上にしたためSceneDelegateに書いていきます.

SceneDelegate.swift
import UIKit

class SceneDelegate: UIResponder, UIWindowSceneDelegate {

    var window: UIWindow?


    func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
        // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
        // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
        // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
        guard let _ = (scene as? UIWindowScene) else { return }

        // RoutingViewController呼び出し
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            window.rootViewController = UINavigationController(rootViewController: RoutingViewController.init())
            self.window = window
            window.makeKeyAndVisible()
        }
    }
以下略変更なし

ここでUINavigationControllerを設定することで今後画面遷移が楽チンにできるようになります!

ユーザ登録画面へ

そのRoutingViewControllerでユーザ登録画面かログイン画面かの分岐させます.今回はユーザ登録画面から作るのでひとまずユーザ登録画面に遷移するように,openLaunchScreen関数の引数に.registrationを指定しています.

RoutingViewController.swift
import Foundation
import RxSwift

final class RoutingViewController: UIViewController {

    override func viewDidLoad() {
        // ひとまず必ずユーザ登録画面に遷移するように
        self.openLaunchScreen(launchScrenType: .registration)
    }
}

extension RoutingViewController {
    private func openLaunchScreen(launchScrenType: LaunchScreenType) {
        switch launchScrenType {
        case .login:
            // ログイン画面へ移動
            break
        case .registration:
            // ユーザ登録画面へ移動
            RegistrationViewController.start(self)
            break
        }
    }
}

ユーザ登録画面の作成

ユーザ登録画面を作ります.
新規ファイル作成からxibファイルとViewControllerを作成します.今回はRegistrationViewController.swiftRegistrationView.xibという名前にしました.

xibとViewControllerの接続

まず最初にRegistrationViewとRegistrationVCがOutlet接続したりできるようにClassに登録します.xibを開いた時のClassのところにRegistrationViewControllerと入力します.
スクリーンショット 2021-01-27 21.26.17.png

次にFile's OwnerとViewを紐付けます.Outlet接続する感じでFile's OwnerからViewに青線を持ってくると結べます.写真のview-Viewのようにつながってたらできてます!
スクリーンショット 2021-01-27 21.29.55.png

とりあえずtextboxを置いてみる

すごい適当に置いてみました.目立つのでピンク色でど真ん中に配置しています.
スクリーンショット 2021-01-27 21.32.40.png

コードを書く

ユーザ登録ビューを書いていきます.
さきほど配置したtextboxをuserNameという名前でOutlet接続し,viewDidLoad()で赤色にしています.ビュー全体の背景は黄緑にしています.

また,RoutingViewController.swiftで分岐する際

RoutingViewController.swift
case .registration:
    // ユーザ登録画面へ移動
    RegistrationViewController.start(self)

とあったと思います,このstart関数をRegistrationViewController.swiftに書いていきます.
start関数ではnavigationControllerが持っているpushViewControllerというものを使い画面遷移をしています.
currentVCには元々のRoutingViewControllerが入っており,nextVCには遷移先のRegistrationViewControllerが入っています.

RegistrationViewController.swift
import Foundation
import RxSwift

final class RegistrationViewController: UIViewController {
    @IBOutlet weak var userName: UITextField!

    static func start(
        _ currentVC: UIViewController,
        animated: Bool = false
    ) {
        let nextVC = RegistrationViewController(nibName: "RegistrationView", bundle: nil)   // .initは省略可能
        currentVC.navigationController?.pushViewController(nextVC, animated: animated)
        // 上のナビゲーションバーを非表示
        currentVC.navigationController?.navigationBar.isHidden = true
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = .green
        userName.backgroundColor = .red
        print("レジストレーションビューコントローラー")
    }
}

するとこんな感じになります.めちゃめちゃ気持ち悪いですがわかりやすいですね!ちゃんと赤色のtextboxになっています.

ここからはデザインをいい感じに作っていきます.
コードはGitLabにあげています.

next記事

かけたらここに載せます.

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

初めて作るアプリのアイコンを作成する

なぜ記事にするのか

本当に無知(アプリ開発初心者)の段階で、どのような考え方、過程でアプリのアイコンを作ったか記録するため

どんなアプリを作っているのか

以下の記事で説明していますが、台湾中国語および日本語勉強アプリです。アプリのタイトルは「AtaCon」に決めました。
初めて作るアプリのタイトルを決める

要件

・台湾中国語と日本語が学習できるとわかるようにしたい(国旗をいれるとか)

作る過程

YouTubeをやっており、動画編集で使うためAdobe Creative Cloudを契約しています。
そのため、Adobe illustratorでアイコン作成をしました。
完成したアイコンはこれです。
アートボード 1.png

このデザインにセンスがないといことはわかりますが、どうやってセンスのいいアイコンを作れるのかわかりません、、、
とりあえず要件は満たしたのでよしとしますが、いいアイデアがあれば是非教えてください。

作る過程(2021/01/28追記)

上のアイコンが少しごちゃごちゃしていて気に入らなかったのですぐに作り替えました。
アートボード 1.png
テトラポッドでコンクリートを表現しました。

ホーム画面イメージ
Simulator Screen Shot - iPhone 11 Pro - 2021-01-28 at 07.07.17.png

参考URL

【Xcode】アプリアイコンの作成と設定方法
https://qiita.com/digital-creator-masa/items/57ae8e84665205369481

【2021年版】もう配色デザインには迷わない!すごい無料カラーパレットツール72個まとめ
https://photoshopvip.net/72189

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

RxSwift6 の変更点まとめ

RxSwiftの6.0が公開されたので変更点をサンプル付きでまとめました。
GitHubのリリースノートDEV Communityでもまとめられています。

New Logo

dynamicMemberLookup を使用したバインダーの自動合成

今ままでは以下のような ExampleView があったとき

class ExampleView: UIView { 
    var text: String
}

Binder を毎回記述することで

extension Reactive where Base: ExampleView {
    var text: Binder<String> {
       Binder(base) { base, title in 
           base.text = text
       }
    }
}

このように stream をクラスに bind することができました。

let exampleView = ExampleView()
Observable.of("text").bind(to: exampleView.rx.text)

RxSwift 6 からは、どのクラスに対しても、Binderを自動的に合成します。そのため、上記のBinderのコードをすべて削除してコードをすっきりさせることができるようになりました。

Infallible

エラーを流さない Observable です。

Infallible<String>.create { observer in
    observer(.next("next"))
    observer(.completed)
    // これはコンパイルエラー
    // observer(.error(NSError()))

    return Disposables.create {}
}

Observable と比べると微妙にメソッドが違います。

Observable<String>.create { observer in
    observer.on(.next("next"))
    observer.on(.error(NSError()))
    observer.on(.completed)

    return Disposables.create {}
}

また RxCocoa の DriverSignal は常に MainScheduler を使用しリソースを共有するのに対して、Infallible、基本的に Observable である点が異なります。

drive()emit() で複数の observerrelay にバインドできるようになりました

let myButton = UIButton()
Driver.of(true).drive(myButton.rx.isEnabled, myButton.rx.isSelected)
Signal.of(true).emit(myButton.rx.isEnabled, myButton.rx.isSelected)

decode(type:decoder:) が追加されました

Combineと同様に、Dataを出力するObservableに対して動作するデコード演算子が追加されました。

service
    .fetchJSONUsers() // Observable<Data>
    .decode(type: [Example].self, decoder: JSONDecoder()) // Observable<[Example]>

SingleSwiftResult を使うようになりました。

subscribe の命名が変わったため警告が出るようになりました。

// RxSwift 5
// 'subscribe(onSuccess:onError:onDisposed:)' is deprecated: renamed to 'subscribe(onSuccess:onFailure:onDisposed:)'
single.subscribe(
    onSuccess: { value in
        print("Got a value: \(value)")
    },
    onError: { error in
        print("Something went wrong: \(error)")
    }
)

// RxSwift 6
single.subscribe(
    onSuccess: { value in
        print("Got a value: \(value)")
    },
    onFailure: { error in
        print("Something went wrong: \(error)")
    }
)

ReplayRelay が追加されました

新しく ReplaySubject をラップした ReplayReplay が追加されました。
BehaviorRelayPublishRelay と同様にエラーを流しません。

withUnretained が追加されました

いままでは weak キーワードを使い循環参照をせずにクラスに値を渡していました。

viewModel.importantInfo
    .subscribe(onNext: { [weak self] info in 
        guard let self = self else { return }
        self.doImportantTask(with: info)
    })
    .disposed(on: disposeBag)

RxSwift 6 からは withUnretained を使うことで綺麗に記述することができます。

viewModel.importantInfo
    .withUnretained(self) // (self, info) のタプルになる
    .subscribe(onNext: { owner, info in 
        owner.doImportantTask(with: info)
    })
    .disposed(by: disposeBag)

distinctUntilChange(at:) で Key Paths をつかるようになりました。

Observable.of(Example(text: "1"), Example(text: "2")).distinctUntilChanged(at: \.text)

struct Example {
    let text: String
}

DisposeBag のビルダー関数でSwiftUIのようにカンマを使わずに記述することができます。

var disposeBag = DisposeBag { 
    observable1.bind(to: input1)

    observable2.drive(input2)

    observable3.subscribe(onNext: { val in 
        print("Got \(val)")
    })
}

disposeBag.insert {
    observable4.subscribe()

    observable5.bind(to: input5)
}

ignoreElementsObservable<Never> を返すようになりました

※ 修正前

public func ignoreElements() {
    -> Completable {
        return self.flatMap { _ in
            return Observable<Never>.empty()
        }
        .asCompletable()
}

※ 修正後

public func ignoreElements() {
    -> Observable<Never> {
    self.flatMap { _ in Observable<Never>.empty() }
}

UIApplicationRxExtension が追加されました

以下変数が追加されました

  • didEnterBackground
  • willEnterForeground
  • didFinishLaunching
  • didBecomeActive
  • willResignActive
  • didReceiveMemoryWarning
  • willTerminate
  • significantTimeChange
  • backgroundRefreshStatusDidChange
  • protectedDataWillBecomeUnavailable
  • protectedDataDidBecomeAvailable
  • userDidTakeScreenshot

変更のコミット

命名変更

メソッドのリネームがありました。

RxSwift 5 RxSwift 6
catchError(_:) catch(_:)
catchErrorJustReturn(_:) catchAndReturn(_:)
elementAt(_:) element(at:)
retryWhen(_:) retry(when:)
takeUntil(_:) take(until:)
takeUntil(behavior:_:) take(until:behavior:)
takeWhile(_:) take(while:)
takeWhile(behavior:_:) take(while:behavior:)
take(_:) take(for:)
skipWhile(_:) skip(while:)
takeUntil(_:) take(until:)
observeOn(_:) observe(on:)
subscribeOn(_:) subscribe(on:)

前のメソッドを使ってもエラーにはならずに以下のような警告が表示されます。

'catchError' is deprecated: renamed to 'catch(_:)'
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初めて作るアプリのタイトルを決める

なぜ記事にするのか

本当に無知(アプリ開発初心者)の段階でどのようにタイトルを決めたのか、記録を残しておくため。

どんなアプリを作っているか

台湾中国語の学習アプリをつくっています。
ForGif.gif

日本語文章とその翻訳を一対にしており、?を押すことで台湾中国語が表示できるようになっています。
「Next」と「Back」で文章を変更します。
右上の「lang」を押すことで、日本語と台湾中国語を切り替えることができます。
また、文章の表示と同時に録音音声を鳴らしています。
ちなみにですが、日本語音声は僕、台湾語音声は僕の彼女が台湾人なのでお願いしています。

録音音声の処理はpythonで書いています。

pythonを使って録音した音声を採番、無音部分をカットする【プログラミング超初心者】
https://qiita.com/atamakonkurii/items/af8cddaccf4565361086

制約

日本人と台湾人に使ってもらいたいので、英語のタイトルがいいかなと考えました。

考える過程

以下の記事によるとアプリのタイトルは3つ考える必要がありそうです。
アプリ製作者必見!アプリタイトルを決めるのに必要な情報まとめ

1.アプリ名(呼称)
2.ホーム画面の下にあるタイトル
3.ストア掲載されるタイトル

メルカリを例にして説明すると
1は「メルカリ」
2は「メルカリ」
3は「フリマアプリ-メルカリ フリマでかんたんショッピング」
となります。

yahoo!乗り換え案内の場合は
1は「Yahoo!乗り換え案内」
2は「Y!乗換案内」
3は「Yahoo!乗換案内 無料で路線図や乗り換えの情報を検索できるアプリ」
といった感じです。

言語学習アプリということを伝えたいので、あまり捻らずに行こうかと思います。

思いましたが、以下のタイトルに決定しました。

1.「AtaCon」
2.「AtaCon」
3.「AtamaConcrete 台湾中国語、日本語勉強アプリ」

読み方は「アタコン」です。
日本ではすでに使われていない日本語ですが、台湾には日本統治時代の名残りで「頭コンクリート」という言葉が残っているそうです。
意味は頭が固いとかそんな感じらしいです。
日本と台湾の言語的な文化のつながりが垣間見えるタイトルなのではないかと思いこれに決めました。
すぐ変えるかもしれません、、、

ちなみに僕がやってるYouTubeのチャンネル名は「頭コンクリ」です。
狙ってやったわけじゃなく、けっこうこの言葉が好きで気に入ってるから結局アプリの名前もこれ関連になってしまいました。
プログラミングと全然関係ないですが、お時間あれば覗いてみてください。
https://www.youtube.com/channel/UC2JHTgLF0wHhkKIPS9sTl0w

まとめ

結局かなりテキトーに決めてしまいましたが、記録しておきます。

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

ViewcontrollerからTabBarControllerへコードで移動する

ViewControllerからTabBarControllerに移動する

まず、最初に下の図のようにViewControllerからTabBarControllerに紐づいてた緑のViewControllerに遷移しても、ViewControllerへの移動なので、下のタブが表示されません。だから、TabBarViewControllerへ移動する必要があります。

コード

ViewController.swift
import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

    }
    @IBAction func FromViewControllertoTabBarController(_ sender: Any) {

        let storybord=UIStoryboard(name: "Main", bundle: nil)
        let TabBarController=storybord.instantiateViewController(withIdentifier: "TabBarController") as! UITabBarController
        //[0]が緑のViewControllerで[1]にすると赤のViewControllerに遷移する
        let ViewController=TabBarController.viewControllers?[0] as! FirstViewController
        //TabControllerに遷移した時に最初にどの画面を表示するかを選択する
        TabBarController.selectedViewController=ViewController
        TabBarController.modalTransitionStyle = .crossDissolve
        TabBarController.modalPresentationStyle = .fullScreen
        self.present(TabBarController, animated: true, completion: nil)
    }

}

赤色のViewControllerにはFirstViewControllerクラスが紐づいています。

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