日々、徒然プログラミング。

都内のIT系専門学校に通う、一人暮らし女子学生chocoffeeの、 日々の気づきと学び、たまにほっこりを綴るブログ。

エンジニアのみなさま、エンジニアの卵のみなさま、IT業界のみなさまが
わたしの「チームってなんだ?」というぼやきに反応してくださることを祈って。

こんにちは!
腰痛と肩こりがMaxなちょこひです。

作業時間が長かったここ最近。
マッサージ行きたい。

 
Twitterクライアント作れた!!

Swiftの授業課題で、
Twitterクライアントアプリを作成して発表会をする
というものが出ました。

期限は10日間。


・・・期限は、10日間。


(((( ;゚д゚)))



頭を抱える友達、瞳孔が開いてる友達、
逆に開き直って諦める友達。

わたしは瞳孔が開いたタイプ。


やるっきゃねえわな!?!?

野球部もびっくりのど根性精神、解禁です。 


 
企画

授業では、

・タイムラインの取得
・新規ツイート方法
・RT方法

あたりを扱いました。jsonの受け取り方・投げ方は一通りわかっている状態ですね。
ここから、自分なりに色々考えて機能追加していくわけです。

課題ではありますが、
どうせやるなら自分の好き勝手やりたい!」と思ったので、
好き勝手やる事にしました。

普段は公式のアプリを使っているのですが
不満点がぎょーさんあるんです。

・新規投稿が右上にある問題
 →iPhone6Sを使っているせいか指が届かなくて困る
・検索が投稿の隣にある問題
 →普段検索は滅多に使わないのに投稿と間違えて押す事が多くてストレス
・TLでのリプライ表示邪魔すぎ問題
 →TLは時間通りに並んでいた方が見やすい
・背景真っ白固定問題
 →黒背景に白文字の方が見やすいタイプなので、白背景は辛い
・自分のふぁぼ一覧遠い問題
 →自分がいいねしたものをよく見返すけど、そこまでが遠い
・ストリーミング非対応問題


などなど。

自力で出来る部分に関しては
自分の理想通りに作ってやろうと決めました。

・画面下半分のみで操作可能なUI
・黒背景
・紫が好きだから紫入れる
・いいねしたTLは1タップでアクセスできるようにする


これらを実現してみました。



ひとまず形にしたアプリ


01

アカウントを選択して、


07


UITabBarControllerに遷移します。
左から タイムライン、メンション、新規投稿、いいねしたタイムライン、現在のアカウント情報
が表示されます。

一度にとってくるツイートは20件で、
一番下の行が出てきたときに続きを読み込む様にしています(たまにバグる、なんでえ)

45


新規投稿はこんな感じ。
画像も1枚なら添付できます。


58

ツイート詳細画面。
リプライ、RT、ふぁぼ、ツイ消し機能がついています。
RTとふぁぼの数も表示されます。

鍵垢の場合はRTできないとか、
自分以外のツイートの削除もできないとか、
その辺も頑張っています。本当に頑張った・・・(遠い目)



ソースコードの話


まだ完成品ではありませんが、
現段階でのソースコードはGitHubにあげています。

GitHubを見る

どう実装したのかは見ていただければお分かりいただけるかと思いますし、
長くなってしまうのもアレなので
実装する上で大変だった事を書いていこうと思います。

いや本当に・・・大変だったんだ・・・

写真 2016-05-28 13 44 12

ほら、大変そうでしょ?(´;ω;`)


TableViewの最終行が表示されたら追加読み込みapiを投げていたんですが
その判定がおかしかったようで。
HTTPエラーの大安売り。しかも429。

これ、発表会の2日前の出来事。
あと2日しかないのにこれ。

デバッグして、429出て、デバッグして、429でて、アカウント凍結されて、
デバッグして、429出て、デバッグして、429でて、またアカウント凍結されて、
半泣きになって、デバッグして、デバッグして、デバッグデバッグでばでばでばでばbでbだえbdw

という週末を過ごしました。

発表会30分前に思いついて使うメソッドを変えたら
すっきり解決しやがったので、まあ、うん、よし。

 
今後の計画


10日間という短い期限なのもあって、
クライアントとして必要不可欠な機能の幾つかを
まだ実装できていません。

・フォロー機能
・フォロワー / フォローの人一覧
・リスト機能
・プロフィール編集
・DM機能

以上は手付かずな状態。
TLみてちょこっとつぶやくだけ、のようなアプリどまりです。 


これでやめるわけにいかんよなあ!?!?(ど根性精神2回目)


と、いうことで夏休み中を目処に色々機能を追加して 、
AppStoreリリースを目指します。

リジェクトされても、とにかく完成させる。

GooglePlayにはリリース経験あるので、
AppStoreにも出しておきたい!


つきましては、アイコンとLaunchScreenの作成に協力してくださる
デザイナーさんを募集します。


「やってやらんでもないぜ」って方は、ご連絡ください。



まとめ
 

今回学んだ事

・継承とプロトコルの偉大さ
・jsonすげえ
・apiのドキュメントちゃんと読むことの大事さ
・自分の英語力の低下具合 
・デバッグは腰にくる
・HTTP429エラーの恐怖 

HTTP429エラーと早く絶交できるように
続きもがんばっていきます。

寝不足が解消されない・・・



ちょこひ 

こんにちは!
ポケモン新作の情報解禁でアドレナリンがっぽがぽなちょこひです。
御三家は最初は炎のニャビーかな〜!
どうせサンもムーンも買うからコンプリートするけども!(・∀・) 

 
今日のテーマ!

開設以来Swiftの記事しか書いていなくて、
iOS苦手なのがバレAndroidを放置していたので
今回はAndroidJavaをやります! 

テーマは、ズバリ「Adapter」。

以前Swiftの記事でTableViewControllerについてやりましたが、
AndroidでもListViewを使ってなんやかんやするアレをアレします。

個人的に初見の時はかなり手こずったので、覚え書きとして。 


 
テストアプリ詳細


とある大学のとあるクラスの名簿データjsonファイル(ローカル)から
Adapterを使いListViewに表示。
画面上部のスピナーでクラスを変更する。

以下ss。
ssA


スピナーの値を変えると、


ssB


となります。



以下、用意したjsonファイル。

{
"students": {
"classA": [
{
"no": "IT0101",
"name": "愛 飢え夫",
"type": "いつも悲しい顔をしている。"
},
{
"no": "IT0102",
"name": "華菊 ヶ子",
"type": "クラスのマドンナ。"
},
{
"no": "IT0103",
"name": "差氏 酢背素",
"type": "酢酸を愛している。"
},
{
"no": "IT0104",
"name": "館津 手斗",
"type": "テトリスは世界レベル。"
},
{
"no": "IT0105",
"name": "何抜 寝乃",
"type": "睡眠学習で右に出るものはいない。"
}
],
"classB": [
{
"no": "IT0201",
"name": "ハヒ フヘホ",
"type": "アンパンのような何かに毎日殴られている。"
},
{
"no": "IT0202",
"name": "舞美夢 芽萌",
"type": "クラスのアイドル。かわいい。"
},
{
"no": "IT0203",
"name": "焼ゐ 湯ゑ世",
"type": "焼きおにぎりが好き。"
},
{
"no": "IT0204",
"name": "乱離 流 レロ",
"type": "日本とドバイのハーフ?"
},
{
"no": "IT0205",
"name": "和衣 上ヲ",
"type": "トップスは常に和装。"
}
]
}
}
 
no:学籍番号
name:生徒氏名
type:特徴

とし、すべての情報をListViewの各行に表示させます。


 
XML編


①新規プロジェクトを作成

今回は「AdapterTest」とします。

appName

②activity_main.xmlにパーツを配置

allLayout


layoutTree



③各行に反映させるレイアウトxmlファイルを用意

xml

「New」→「XML」→「Layout XML File」を選び、新規作成します。
タイトルは、「row_item」としました。

rowItem


rowLayout

各パーツにidも振っておきます。


④Spinnerの設定をする

Spinnerに表示する選択肢を設定します。

「res」→「values」→「strings.xml」を開き、
以下を追加します。
<string-array name="spiArray">
<item>A組</item>
<item>B組</item>
</string-array>
配列を作り、各要素をSpinnerに読み込ませる、という流れです。

この配列を、activity_main.xmlにてSpinnerに紐づけます。


<Spinner
android:layout_width="match_parent"
android:entries="@array/spiArray"
android:layout_height="wrap_content"
android:id="@+id/spi"/>
「entries」というステータスがそれに当たります。



Jsonファイルをローカルに保存する

今回はjsonファイルをローカルに使用するので、
プロジェクト自体にぶっこみます。

assets

「New」→「Folder」→「Assets Folder」を選択。
何も変更せず、Finishします。

すると、コンポーネントツリーに「assets」というフォルダができるので、
そこに先ほどのstudents.jsonをペースト。

これでOKです。



 
ソースコード編


・・・の前に、今の私のAdapterの概念を説明します。
信憑性には欠けます。間違えていたらご指摘いただけると幸いです。

blog0511

これの赤文字がAdapterの動き(イメージ)。

表示するデータはjsonファイルに格納されています。
そこからデータを抜き出して、各行のパーツにセットし、
セットしたものをListViewに渡してくれるのがAdapter。たぶん。

つまり超有能。あだぷたーちゃんいい子。


では、ソースコードに行きましょう。


①生徒用のクラスファイルを作成する

makeClass

「New」→「Java Class」より、新規クラスファイルを作成します。
名称は「Student」としました。

public class Student {
private String no;
private String name;
private String type;

public Student() {
}

public String getNo() {
return no;
}

public String getName() {
return name;
}

public String getType() {
return type;
}

public void setNo(String id) {
this.no = id;
}

public void setName(String name) {
this.name = name;
}

public void setType(String type) {
this.type = type;
}
}
private変数を3つ用意し、
それぞれno(学籍番号)、name(氏名)、type(特徴)とし、
それぞれのconstractor、getter、setterを用意。

※フィールドを用意して「command + N」で
getter、setterのショートカット入力ができるよ!!!


②JSONとソースコードの窓口役のクラスを作る
JSONとの連携をひとつのクラスに任せます。
今回はStudentクラスと同様、「JsonHelper」というクラスを作りました。

public class JsonHelper {
public static ArrayList<Student> parseJson(String strJson, int code) {
ArrayList<Student> ary = new ArrayList<>();
try {
String className = "classA";
if(code == 1) className = "classB";
JSONObject json = new JSONObject(strJson);
JSONObject students = json.getJSONObject("students");
JSONArray list = students.getJSONArray(className);
for(int i = 0; i < list.length(); i++) {
JSONObject entry = list.getJSONObject(i);
ary.add(parseToStudent(entry));
}
}catch (Exception e) {
Log.e("JsonHelper", Log.getStackTraceString(e));
}
return ary;
}

public static Student parseToStudent(JSONObject json) throws JSONException {
Student tmp = new Student();
tmp.setNo(json.getString("no"));
tmp.setName(json.getString("name"));
tmp.setType(json.getString("type"));
return tmp;
}

JsonはDictionaryの様な構造をしています。
キーを指定し、その中身を引っ張ってくる、という流れでデータを受け取ります。

まずは、ArrayList<String> aryにjsonの中身をぶっこみます。

try内、「className」及びその下のif分は、
Spinnerにより表示するクラスを変えるためのものです。
デフォルトではclassAを指定し、初期化する際の引数codeにより判別します。
つまり、MainActivity.javaにてcode変数を変更します(後述)。

jsonを受け取る→その中のstudentsを受け取る→その中からA組かB組どちらかの名簿をlistに格納。
for文内でStudentを初期化し、データとして受け取っていく、という流れ。


③jsonファイルを高速で読み込むgetData()メソッドを実装する
ここで愉快な仲間たちが増えます!!!!!
ちなみにまだまだどういう人なのか全然理解出来ていないので、ふわっと説明します。

・InputStream → ファイルを読み込むクラス(今回はjsonファイル)
・BufferedReader → InputStreamをラップして実際にStreamに入れるクラス
・StringBuilder → 可変長のString。本来Stringは非可変長なため、追加回数が多い場合はStringBuilderで処理をして、最後にStringにキャストしたほうが処理が高速になる。

private String getData() {
String json = "";
BufferedReader br = null;
try {
InputStream in = getAssets().open("students.json");
br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line);
}
json = sb.toString();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}finally {
try {
if(br != null) br.close();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}
}
return json;
}

JsonHelperに引数として渡すメソッドです。
JsonhelperはString型にしたJsonデータと、今回独自に追加したSpinner判定のint型を引数に取ります。
assetsからMainActivity.javaを経由してJsonHelperにアクセスし、Studentにパースして、それらをAdapterによりListViewにセットする、という流れ。

④Adapterを用意する
class RowModelAdapter extends ArrayAdapter<Student> {
RowModelAdapter(Context con) {
super(con, R.layout.row_item);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Student item = getItem(position);
if(convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(R.layout.row_item, null);
}
if(item != null) {
TextView txtNo = (TextView)convertView.findViewById(R.id.txtNo);
if(txtNo != null) {
txtNo.setText(item.getNo() + ":");
}
TextView txtName = (TextView)convertView.findViewById(R.id.txtName);
if(txtName != null) {
txtName.setText(item.getName());
}
TextView txtType = (TextView)convertView.findViewById(R.id.txtType);
if(txtType != null) {
txtType.setText("\t" + item.getType());
}
}
return convertView;
}
}
 Adapterに各行のレイアウトを読み込ませます。
convertViewとは、その読み込まれたViewを指します。
convertViewの中の、それぞれのパーツに、Studentクラスのgetterを使って値をセットしていきます。

⑤ListViewにAdapterをセットする 

private int code = 0;
public void setList() {
RowModelAdapter adapter = new RowModelAdapter(this);
ArrayList<Student> students = JsonHelper.parseJson(getData(), code);
for(Student tmp: students) {
adapter.add(tmp);
}
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(adapter);
}
今回はSpinnerの値が選ばれるごとにListViewを再読み込みさせるため、メソッド化しています。
読み込みが一度でよければ、onCreate()内に記述します。

adapterに各データをaddし、listがそれを参照する様にします。



⑥Spinnerの値が選ばれた時のメソッドを用意する

 
class spinnerValueChanged implements AdapterView.OnItemSelectedListener {
    @Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
code = position;
setList();
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
}

OnItemSelectedListenerは、Spinnerの値が変更された時に呼ばれます。
onItemSelected、onNothingSelectedは必ず実装しなければなりませんが、
今回は必要無いので空っぽのメソッドのままにしています。

これをonCreate()でSpinnerにセットしてあげれば、OK。

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setList();

Spinner spi = (Spinner)findViewById(R.id.spi);
spi.setOnItemSelectedListener(new spinnerValueChanged());
}





ソースコードまとめ


・MainActivity.java
public class MainActivity extends AppCompatActivity {

private int code = 0;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

setList();

Spinner spi = (Spinner)findViewById(R.id.spi);
spi.setOnItemSelectedListener(new spinnerValueChanged());
}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();

//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}

return super.onOptionsItemSelected(item);
}

private String getData() {
String json = "";
BufferedReader br = null;
try {
InputStream in = getAssets().open("students.json");
br = new BufferedReader(new InputStreamReader(in));
StringBuilder sb = new StringBuilder();
String line;
while((line = br.readLine()) != null) {
sb.append(line);
}
json = sb.toString();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}finally {
try {
if(br != null) br.close();
}catch (Exception e) {
Log.e("Main", Log.getStackTraceString(e));
}
}
return json;
}

class RowModelAdapter extends ArrayAdapter<Student> {
RowModelAdapter(Context con) {
super(con, R.layout.row_item);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
Student item = getItem(position);
if(convertView == null) {
LayoutInflater inflater = getLayoutInflater();
convertView = inflater.inflate(R.layout.row_item, null);
}
if(item != null) {
TextView txtNo = (TextView)convertView.findViewById(R.id.txtNo);
if(txtNo != null) {
txtNo.setText(item.getNo() + ":");
}
TextView txtName = (TextView)convertView.findViewById(R.id.txtName);
if(txtName != null) {
txtName.setText(item.getName());
}
TextView txtType = (TextView)convertView.findViewById(R.id.txtType);
if(txtType != null) {
txtType.setText("\t" + item.getType());
}
}
return convertView;
}
}

public void setList() {
RowModelAdapter adapter = new RowModelAdapter(this);
ArrayList<Student> students = JsonHelper.parseJson(getData(), code);
for(Student tmp: students) {
adapter.add(tmp);
}
ListView list = (ListView)findViewById(R.id.list);
list.setAdapter(adapter);
}

class spinnerValueChanged implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
code = position;
setList();
}

@Override
public void onNothingSelected(AdapterView<?> parent) {

}
}

・Jsonhelper.java

public class JsonHelper {
public static ArrayList<Student> parseJson(String strJson, int code) {
ArrayList<Student> ary = new ArrayList<>();
try {
String className = "classA";
if(code == 1) className = "classB";
JSONObject json = new JSONObject(strJson);
JSONObject students = json.getJSONObject("students");
JSONArray list = students.getJSONArray(className);
for(int i = 0; i < list.length(); i++) {
JSONObject entry = list.getJSONObject(i);
ary.add(parseToStudent(entry));
}
}catch (Exception e) {
Log.e("JsonHelper", Log.getStackTraceString(e));
}
return ary;
}

public static Student parseToStudent(JSONObject json) throws JSONException {
Student tmp = new Student();
tmp.setNo(json.getString("no"));
tmp.setName(json.getString("name"));
tmp.setType(json.getString("type"));
return tmp;
}
}


・Student.java

public class Student {
private String no;
private String name;
private String type;

public Student() {
}

public String getNo() {
return no;
}

public String getName() {
return name;
}

public String getType() {
return type;
}

public void setNo(String id) {
this.no = id;
}

public void setName(String name) {
this.name = name;
}

public void setType(String type) {
this.type = type;
}
}



まとめ


長くなりました!
こうしてまとめてみると、まだあやふやなところが目立ちますね。
理解し次第、差し替えていこうと思います。

p.s.最近腰痛が本当に辛いです。なんとかして。




ちょこひ 



こんばんは!
猫になりたいちょこひです。

今みたいに充実した日々ももちろん楽しいけど、
たまに猫になって日向でごろにゃーしたくなる。

 
今回作成するテストアプリ 

前回記事の通り、今回はDelegateを使ったサンプルアプリを作成します。
全体的な動きは
・Viewは2つのみ
・1つ目のViewにtextFieldとButtonを配置、Buttonから2つ目のViewにModalとして遷移
・2つ目のViewに LabelとButtonを配置、1つ目のTextFieldに入力された文字列をLabelに表示、Buttonで2つ目のモーダルビューを閉じる

という簡単なものです。

使うDelegateは
2つ目のモーダルビューを閉じる」部分と、
textField入力時、キーボードのreturnボタンでキーボード自体を閉じる
の2点です。

キーボード自体を閉じる動きはDelegateの鉄板。

Segueを通して文字列を渡す動きは、前回のTableViewControllerの復習を兼ねています。 

以下ss。

38



43

returnキーでキーボードの終了。

46

sendボタンで画面遷移。


50

backボタンでモーダルビューの終了。


では、行きましょう。

 
StoryBoard


・ViewController
09
サイズは適当です。上部にtexiField、下部にButtonを置いて、sendに変更。



・ModalViewController

38

こちらも適当。
わかりやすくするために背景色を変えています。


・sendボタンからModalViewへSegueをつなぐ

presentModaly


前回は「Show」を選択していましたが、今回は「Present Modally」を選択。
これで、2つ目のViewはモーダルビューになります。


・SegueにIdentifierを設定

24


今回はmodalSegueというIdentifierをつけました。


・新規ファイル「ModalViewController.swift」を作成

16

今回もcocoaTouchで作成しました。


・ModalViewControllerとModalViewController.swiftをつなげる

44

storyBoardにて参照先クラスを設定します。




・各クラスにそれぞれのパーツをつなげる

2画面モード(名称なんでしたっけ)にして、

ViewControllerのtextField(Outlet)
ModalViewControllerのLabel(Outlet)とButton(Action) を
つなげます。
 
textField
32
「textField」という名称にしました。


Label
06
「label」という名称にしました。


Button
16
「backToFirst」という名称にしました。


これで、ひとまずは準備OK!



 
ModalViewController.swift


では、ソースコードをいじっていきます。

2つありDelegateのうち、ModalViewを閉じる方は

処理を依頼する側=ModalViewController.swift
処理を依頼される側=ViewController.swift 

です。 まずはこちらを先に実装します。


・protocolの宣言

前回記事の通り、protocolとは「依頼する文書」のことです。これを仲介して実際に依頼します。

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


今回は、protocolの名称を「dismissModal」とし、
実際に依頼する処理名を「dismiss」としました。

この時、dismiss()メソッドには何も書きません。引数と、その型、あるなら戻り値のみ書きます。
Javaでいうinterfaceと同じです。

最近、「protocol extention」とかいうものが出たらしく
AndroidJavaでいう抽象クラスのようなものがあるらしいです。聞かなかったことにします。

このprotocolを2つのクラスでなんやかんやして行きます。

具体的には

ModalViewController.swift「自分自身の終了はできないから、ViewControllerに終了を依頼します!」
ViewController.swift「その依頼書受けます!いつでも来やがれ!」

ってな状態にして行きます。


・「私ModalViewControllerはdismissModalプロトコルを持っています!」と宣言

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


class ModalViewController: UIViewController {

    var delegate: dismissModal?


以降、変数delegateはプロトコルを参照するようになります。


・ついでにViewControllerから文字列を受け取るための変数を宣言

//  ModalViewController.swift

protocol dismissModal {

    func dismiss()

}


class ModalViewController: UIViewController {

    var delegate: dismissModal?

    

    @IBOutlet weak var label: UILabel!

    var text: String = ""


今回は、String型変数textを用意しました。

・Labelに変数textをviewDidLoad()で代入

//  ModalViewController.swift

    override func viewDidLoad() {

        super.viewDidLoad()

        label.text = text


        // Do any additional setup after loading the view.

    }


これで、Labelへのデータ受け渡し準備は完了です。


 ・実際に処理を依頼する
モーダルビューの終了は、backボタンで行います。
つまり、backボタンが押されたら、依頼をすればいいですね。

//  ModalViewController.swift

    @IBAction func backToFirst(sender: UIButton) {

        delegate?.dismiss()

    }


オプショナル型にしているのは、
protocolの受け取り先(今回でいうViewController.swift)でdismiss()メソッドが実装されていない場合があるためです。


ここまでで、依頼書を投げるところまでは完了しました。


 
ViewController.swift


次は、受け取る側の処理です。

protocolとは、直訳すると「規約」です。
Javaのinterfaceと同じで、protocol内のメソッドは実装しなければなりません。

受け取った依頼書には、
「dismiss()メソッドを実行してください」と書いてある感じです。
なので、このクラス内でdismiss()メソッドを用意する必要が有ります。


・dismissModalプロトコルを継承

//  ViewController.swift

class ViewController: UIViewController, dismissModal {

 

・継承したprotocol内のメソッド、つまりdismiss()メソッドを実装

//  ViewController.swift

    func dismiss() {

        self.dismissViewControllerAnimated(true, completion: nil)

    } 


・ModalViewControllerの処理の依頼先は自分だ!という宣言

//  ViewController.swift

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "modalSegue" {

            let nextViewController = segue.destinationViewController as! ModalViewController

            nextViewController.delegate = self

        }

    } 


前回のUITableViewControllerでも、遷移先の変数への代入の際に、似た書き方をしました。
今回も復習のために同じことをやるので、

・Segueを通してModalViewController.swiftの変数への代入

//  ViewController.swift

    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {

        if segue.identifier == "modalSegue" {

            let nextViewController = segue.destinationViewController as! ModalViewController

            nextViewController.delegate = self

            nextViewController.text = textField.text ?? "nil"

        }

    }


こうしてしまいましょう。
これで、モーダルビューのDelegate処理が完了しました。
 

キーボードを閉じる処理


残るもうひとつの処理では、
処理を依頼する側=textField
処理を依頼される側=ViewController.swift
となります。

先ほどはプロトコルを作成しましたが、今回はその必要はありません。
すでにtextFieldクラスには、プロトコルが存在しています。

//  UITextField.h
public protocol UITextFieldDelegate : NSObjectProtocol {

    

    @available(iOS 2.0, *)

    optional public func textFieldShouldBeginEditing(textField: UITextField) -> Bool // return NO to disallow editing.

    @available(iOS 2.0, *)

    optional public func textFieldDidBeginEditing(textField: UITextField) // became first responder

    @available(iOS 2.0, *)

    optional public func textFieldShouldEndEditing(textField: UITextField) -> Bool // return YES to allow editing to stop and to resign first responder status. NO to disallow the editing session to end

    @available(iOS 2.0, *)

    optional public func textFieldDidEndEditing(textField: UITextField) // may be called if forced even if shouldEndEditing returns NO (e.g. view removed from window) or endEditing:YES called

    

    @available(iOS 2.0, *)

    optional public func textField(textField: UITextField, shouldChangeCharactersInRange range: NSRange, replacementString string: String) -> Bool // return NO to not change text

    

    @available(iOS 2.0, *)

    optional public func textFieldShouldClear(textField: UITextField) -> Bool // called when clear button pressed. return NO to ignore (no notifications)

    @available(iOS 2.0, *)

    optional public func textFieldShouldReturn(textField: UITextField) -> Bool // called when 'return' key pressed. return NO to ignore.

}


「UITextFieldDelegate」というものです。
これをVIewControllerで継承します。

・UITextFieldDelegateを継承し、viewDidLoad()内で引き受け宣言をする

//  ViewController.swift

class ViewController: UIViewController, dismissModal, UITextFieldDelegate {

    

    @IBOutlet weak var textField: UITextField!


    override func viewDidLoad() {

        super.viewDidLoad()

        textField.delegate = self

        // Do any additional setup after loading the view, typically from a nib.

    }

 
あとは、「キーボードを閉じる」処理を実装すればOKです。


・textFieldShouldReturnメソッドを実装する

//  ViewController.swift

    func textFieldShouldReturn(textField: UITextField) -> Bool {

        textField.resignFirstResponder()

        return true

    }

 

以上で、完了となります。


まとめ


前回のTableViewControllerと似た部分もあったので、
今回は前回よりも簡略化してまとめてみました。 

アウトプットすることで、自分の頭の中も整理されますね。
ちょっと残っていたごちゃごちゃが解消されました!


依頼する側・依頼される側で分けて考えると、
比較的理解しやすいと思います。


はー!ひとつずつ着々と疑問点を解消していこうー!がんばるぞおー!




ちょこひ 

↑このページのトップヘ