Swiftで遊ぼう! on Hatena

あしたさぬきblogでやってた初心者オヤジのiOSプログラミング奮闘記がHatenaに来ました

Swiftで遊ぼう! - 977 - Good design is not easy

WWDC 2017が来月開催されるというのに私はWWDC 2016のビデオを見て勉強しています。昨日berrymuchさんからiOSユーザーインターフェイスガイドラインの説明を受けたので次のビデオを見ました。

developer.apple.com

おおお、なんとKeynoteでデザインしているんですね。Keynoteは私もプレゼンテーションで使いまくっているので慣れています。これから作るアプリもKeynoteを使ってデザインしてみます。KeynoteのデザインをKeynoteでするほどアップルの人間はKeynote好きなんですね。

Design mattersです。やっぱりデザインの勉強もしないといけませんね。

ということで今日からデザインに目覚めます(笑)

では。

Swiftで遊ぼう! - 976 - セグエに関するコメントの返事

久しぶりに下記のエントリーにコメントが入りました!

私がブログを続けている理由の1つは読者からのコメントです。berrymuchさん、本当にありがとうございます!

yataiblue.hatenablog.com

今日の記事は上記ブログの内容の続きです。

berrymuchさんから2つ指摘を受けました。まず最初のprepareメソッドに関して詳しく説明します。

私の理解している範囲でセグエの説明をします。

セグエのインスタンス化には以下の2つの方法があります。

  1. トーリーボードで視覚的に生成(Ctrl + ドラッグ)
  2. コードで生成(performSegueを書く)

このどちらを利用してセグエを生成しても、遷移先のViewController生成メソッドは「prepare(for segue: UIStoryboardSegue, sender: Any?) 」です。そのため、このメソッドをoverrideしなければいけません。

私は最初に、ストーリーボードで視覚的にセグエを設定しました。つまりTextFieldから「Ctrl + ドラッグ」して遷移先のViewControllerに繋いで、Identifierの設定をしたということです。

そして遷延元のViewControllerに次のコードを加えました。

override func prepare(for segue:UIStoryboardSegue, 
                         sender:Any!) {
        
    if let identifier = segue.identifier {
        switch identifier {
        case "Show Datepopover":
             if let dateController = segue.destination 
                                as? DatePopoverController {
                 dateController.delegate = self
                 let widthX = birthTextField.bounds.midX
        
                 if let popoverController = 
                        dateController.popoverPresentationController {
                     popoverController.delegate = self
        
                     popoverController.sourceRect = 
                         CGRect(x: widthX, 
                                y: self.view.frame.minY + 30, 
                            width: 0, 
                           height: 0)
                 }
        
             }
         case "Show Numpadpopover":
             if let numpadPopover = 
                    segue.destination as? NumpadViewController {
                  numpadPopover.delegate = self
        
                  let widthX = idField.bounds.midX
        
                  if let popoverController = 
                      numpadPopover.popoverPresentationController {
                      popoverController.delegate = self
        
                      popoverController.sourceRect = 
                            CGRect(x: widthX, 
                                   y: self.view.frame.minY + 30, 
                               width: 0, 
                              height: 0)
                  }
              }
        
        
          default:
               break
           }
      }
}

これでシュミレーションを実行させてテキストフィールドをタッチすると、デフォルトのキーボードが出現するんです。このデフォルトのキーボードをマニュアルで閉じると遷移先ののPopoverが出現します。

どうしてもデフォルトのキーボードが最初に立ちあがるのを止めることができません。色々試していて上手く動いたのが先日お話したperformSegueの利用だったと言うわけです。

デフォルトのキーボードの消し方も色々あることは分かりました。resignFirstResponder()textFieldShouldBeginEditing(_ textField: UITextField)を色々使って、試していたらperformSegueで上手く動いたということです。

キーボードの制御に関して考えていると、berrymuchさんの2つめのアドバイスで「は!」と気がつきました。私の考えているデザインは、インターフェイスガイドラインに全く準じていませんでした。

ユーザインターフェイスのデザインのヒント - Apple Developer

このページも全く知らなかったです。berrymuchさん、情報ありがとうございます。

カスタムPopoverを使おうと考えたのは、私のアプリはiPad専用を考えていたからです。iPhoneで「Number Pad」を利用すれば数字だけの入力ができるのですが、iPadでは「Number Pad」の利用ができません。そこで独自にPopoverからの入力を思いついた訳です。

しかし、ガイドラインを読むと、そういう勝手なインターフェイスは許されていないようですね。「TextFieldからPopoverのコントロールガイドライン違反」と考えていいんでしょう。それで実装しにくいんじゃないかと思います。

そして思いついたんです。iPadで用意されていないのならカスタムキーボードを用意して入力すればガイドラインに準じていると思いました。

berrymuchさんのコメントでまた少し成長できました。ありがとうございました。

通りすがりの読者の皆さん、何でもいいのでコメントをお願いしますm(_ _)m

では。

Swiftで遊ぼう! - 975 - Numpad Popoverの実装に関して

誕生日を入力するためのDate PopoveはViewControllerに値を渡さないといけないのでProtocolを用意しました。ViewControllerはこのプロトコールに準拠することで値を表示することができます。このやり方だと準拠すべきプロトコールがクラス宣言の後に並びます。UITextFieldDelegateやUIPopoverPresentationControllerDelegateも並ぶので長々しく見た目はよくありません。

カスタムPopoverを1つ作るたびにProtocolに準拠させるというコーディングスタイルは正しいのでしょうか?かなり疑問に思っています。

Numpad Popoverという新たなカスタムPopoverを用意しました。この入力値をViewControllerに渡すためにProtocolを用意しました。渡す値の型もDate Popoverと異なるので共通化はできないと思います。ということで今回はExtensionを使ってみました。

f:id:yataiblue:20170513212240j:plain

Numpad Popoverで数字をタップするとリアルタイムにテキストフィールドに表示されるようにできました。

こういう場合Protocolに準拠させるのとExtensionを追加するのとどちらがふさわしいのでしょう?

悩みはつきません(^_^;)

Swiftで遊ぼう! - 974 - Popoverのサイズ考察

Popoverのサイズの調整もできるようになりました。

また一歩前進。

f:id:yataiblue:20170509181417j:plain

先日まで無駄に大きく広がっていたPopoverのウインドウサイズを次のように変更できました。

f:id:yataiblue:20170512083635j:plain

しかし、適当にレイアウトを調整しただけでは駄目なんでしょうね。ボタンの大きさやレイアウトをどうすればいいのかちょっと調べました。

www.dotproof.jp

なるほど、ちょっとしたレイアウト調整でもちゃんと考えて設定した方がいいようですね。上の崩れたレイアウトを次のように変更しました。

f:id:yataiblue:20170512144506j:plain

なんかすっきりしました。

Swiftで遊ぼう! - 973 - Popoverのまとめを改訂

「Swiftで遊ぼう!」シリーズ最初の記事を振り返ってみた。

Swiftで遊ぼう! - 1 class and structure:Life-LOG OtherSide

この記事は2104年7月9日に書かれていますが、あの頃は本当に何も分からず超ド素人でした。

今までコツコツ勉強を続けてここまで理解できたのが不思議です。

「継続は力なり」を実践しているだけ。「毎日ブログ更新」も目標にしてきましたが、これは先日潰えてしまいました(T_T) 

しかし、「継続は力なり」の本質は「諦めない」意気込みです。たとえ失敗しても突き進むのみ。当初はアプリ発表に3年という期間を目標に立てましたが、どうも7月9日までにアプリを発表できそうにもありません(T_T)

それでも続けるだけです!

ということでPopoverの復習をしました。

yataiblue.hatenablog.com

Swiftで遊ぼう! - 972 - 分かった気でいるけど本当はどうなんだろう?

昨日「複数のTextFieldからセグエの切り替えができるようになった!」なんて偉そうな事言っていましたが、本質は理解していないんで、本当に理解できている人がいらっしゃれば教えてもらいたいです。

私の書いたコーディングの概要を説明します。

トーリーボード上のベースになるView Controllerに2つのUITextViewを設置します。ストーリーボードに設置したオブジェクトからViewController.swiftに「Ctrl + ドラッグ」して@IBOutletプロパティを作ります。それぞれ「birthTextField」と「idField」と名前をつけます。次に2つのUIViewControllerをオブジェクト・ライブラからストーリーボードに設置して「Date Popover Controller」と「Numpad View Controller」に変更します。カスタムUIViewControllerはSwiftで遊ぼう! - 270 - カスタムViewControllerの作成法 - Swiftで遊ぼう! on Hatenaで説明しているように用意します。

次にそれぞれのTextFieldからViewControllerに「Ctrl + ドラッグ」してセグエを作ります。Popoverを使いたいので、表示されるメニューから「Present As Popover」を選ぶだけです。ストーリーボードのSegueを選択してIdentifierを「Show Datepopover」と「Show Numpadpopover」にするだけでセグエのインスタンス化がされます。そうなんです。ストーリーボードを使った場合はコードを書かなくてもこれでセグエが作られることになります。

f:id:yataiblue:20170509191211j:plain

ViewController.swiftで「prepare(for segue:UIStoryboardSegue, sender:Any!) 」をオーバーライドするだけでPopoverをインスタンス化できます。

しかし、実際のところ奇妙な現象が生じるんです。

このままランしてシュミレーションを実行します。birthTextFieldとidFieldのどちらかをクリックすると、基本的にデフォルトのキーボードが画面下部からせり出してきます。デフォルトの状態ではキーボードの制御もできません。さらにどちらかのテキストフィールドを選択するとランダムにどちらかのセグエが実行されるためどのTextFieldがクリックされているのか判断させることができないんです。

クリックされたTextFieldを判断させるには以下のデリゲーション・メソッドを使用する必要があります。

func textFieldShouldBeginEditing(_ textField: UITextField)
         -> Bool {
    switch textField {
    case birthTextField:
        performSegue(withIdentifier: "Show Datepopover", 
                             sender: self)
        print("Birth Text Field was selected")
    case idField:
        performSegue(withIdentifier: "Show Numpadpopover", 
                             sender: self)
        print("Id Field was selected")
    default:
        break
    }
    return false
}

これでTextFieldを選別することができます。

しかし、ここで疑問が1つ生じます。「 performSegue(withIdentifier: String, sender: Any?)」はコーディングでセグエを生成する方法であり、ストリーボードを使ってセグエを生成している場合は必要無いはずです。とすれば重複していることになるのですが、performSegueを使用しないでTextFieldを選別できないんでしょうか?

Swiftで遊ぼう! - 971 - UITextFieldを考える

いままで勉強のためにチュートリアルに取り組んでいましたが、いくらチュートリアルをこなしても実践的な能力は身につきませんね。

オリジナルプロジェクトを立ち上げてプログラミングをはじめると、チュートリアルで学んだiOSフレームワークの知識だけでは太刀打ちできず初っぱなから行き詰まってます(T_T)

UITextFieldクラス利用も色々と知らなければならないことがあります。

UITextFieldはUILabelと異なりユーザーからの入力を受け付けて反応することができ、そういう流れからUITextFieldにはデリゲーション・メソッドが用意されています。

プログラミング初心者は「デリゲーション・メソッド」と言われてもちんぷんかんぷんだと思います。今まで基本概念を学んできた私は違和感なく理解できるんですが、それはコツコツとチュートリアルを勉強してきたからです。

デリゲーションは日本語で言えば「委譲」です。委譲はOOP(オブジェクト指向プログラミング)の中核概念の1つです。

オブジェクト指向でなぜつくるのか 第2版

オブジェクト指向でなぜつくるのか 第2版

上記の本によれば、OOPの3大特徴が「クラス」「ポリモーフィズム」そして「継承」です。特にポリモーフィズム(多様性)がiOSフレームワークでふんだんに利用されているんです。

クラス継承を超えたつながりを作るための概念がポリモーフィズムで、Swift言語では「プロトコール&デリゲーション」と「エクステンション」が用意されています。特にプロトコール&デリゲーションは基本中の基本にあたります。
yataiblue.hatenablog.com
デリゲーション・メソッドを利用するためにUITextFieldとViewControllerの繋がりを用意する必要があります。

この準備の仕方にも基本的にストーリーボードの利用とコーディングによる方法があるので混乱しないように注意が必要です。

教科書を読んでチュートリアルをこなせばここまでの基本的な流れは理解できます。しかし、これだけで実践的なコーディングができないんです。

TextFieldからPopoverへのセグエ、入力した値をTextFieldに戻すためにデリゲーションが大活躍します。ここらの詳しい解説をしていませんがグーグルで調べてなんとか自分で解決しました。

そして悩んでいたのが、複数のTextFieldからどうやって1つだけ用意されているデリゲーションメソッドを利用すればいいのか分からなくなっていました。

やっぱりグーグルって素晴らしいですね。調べていると解決できました!

f:id:yataiblue:20170509181417j:plain

誕生日フィールドをタップするとDatePickerが表示されたPopoverが出現します。IDフィールドをタップするとNumberパッドのPopoverが表示されるようになりました。

分かっています。Popoverのレイアウトがむちゃくちゃです。まだStack Viewの絡んだレイアウト調整がちゃんと理解できてないんです。こういう実践的なコーディングの勉強がまだ不十分なんです。

まだまだですね(^_^;)