TableViewでタップした時にキーボードを隠す

このQ&Aのポイント
  • ObjectiveCを用いてiPhnoneアプリ開発の勉強をしています。TableViewControllerのセルにUITextFieldを設置し、そのTextFieldをタッチして表示されたキーボードをキーボードの外がタップされた時に引っ込める(隠す)処理をしたいのですが、うまくできませんでしたので助言をいただきたいです。
  • Tap Gesture RecognizerやtouchesBeganメソッドを試したがUIViewControllerでないとできないようで、キーボードの外をタップしてもキーボードは閉じられなかった。どこのTableViewのクラスをTouchableTableViewに変更したらよいのか分からない。
  • TableViewControllerと作成したTouchableTableViewの関連付けの方法が分からない。Xcode6での新しいクラスの書き方が気になっている。
回答を見る
  • ベストアンサー

TableViewでタップした時にキーボードを隠す

ObjectiveCを用いてiPhnoneアプリ開発の勉強をしています。 TableViewControllerのセルにUITextFieldを設置し、そのTextFieldをタッチして表示されたキーボードを キーボードの外がタップされた時に引っ込める(隠す)処理をしたいのですが、うまくできませんでしたので助言をいただきたいです。 Tap Gesture Recognizerを用いる方法や touchesBeganメソッドを用いてタップを検知し[self.view endEditing:YES];でキーボードを閉じる という方法を試したのですが、これらたUIViewControllerでないとできないようで、 キーボードの外をタップしても、セルが選択されてしまい、キーボードは閉じませんでした。 この問題に関して、以下のサイトが参考になると思ったのですが、 http://onno.jp/dev/2012/02/uitableview-touchesbegan-uiviewcontroller.html >IB で TableView のクラスを TouchableTableView に変更 という部分が、どこを指しているのか分からず、どうしたら良いのか分かりません。 どこのTableViewのクラスを、TouchableTableViewに変更したら良いのでしょうか? 試しにキーボードを引っ込ませる処理を組み込みたいTableViewControllerのスーパークラスを UITableViewController から TouchabeTableViewに変更してみましたが、うまくいきませんでした・・ タップした時にキーボードが引っ込ませたいTableViewControllerと、 新たに作成したTouchableTableViewはどのようにして関連付けるのでしょうか? 以下にTouchableTableViewのソースコードを書いておきます。 (Xcode6になり、Fileの新規作成画面が変わり、クラスの書き方がいつもと違うようになっているのが気になりますが・・・。) ----------- ■TouchableTableView.h #import <UIKit/UIKit.h> @interface UITableView (TouchableTableView) @end ■TouchableTableView.m #import "TouchableTableView.h" @implemention UITableView (TouchableTableView) - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.nextResponder touchesBegan:touches withEvent:event]; [super touchesBegan:touches withEvent:event]; } @end --------- 回答お願いします

質問者が選んだベストアンサー

  • ベストアンサー
回答No.2

あなたが作ったTouchableTableView.mは「カテゴリ」が使われていますが、 既存のメソッド(touchesBegan)をカテゴリで上書きするのは大変危険なので やめた方がよいです。カテゴリを使ってやってよいのは、既存クラスへの メソッドの追加だけです。メソッドのオーバーライドは基本的に禁止と 考えるべきです。 (参考) http://kurokawh.blogspot.jp/2010/03/objective-c.html そもそも、あなたが参考にしたサイトは、カテゴリを使うように書かれていません。 「UITableView のサブクラス TouchableTableView を作成」と書かれており、 カテゴリじゃなくて、UITableViewを継承したサブクラスを作るように書かれています。 もしかして、あなた自身別にカテゴリを使う意図などないのに、 よくわからないまま適当にやって、結果的にカテゴリを使う形に なってしまっているのではないですか? >(Xcode6になり、Fileの新規作成画面が変わり、クラスの書き方がいつもと違うようになっているのが気になりますが・・・。) と書かれていますが、Xcode6になったからといって、別にクラスの書き方は変わっていません。 ただ「New > File」メニューで表示される作成可能なファイル形式の 一覧表示がXcode5と6で若干異なっており、それで混乱しているの ではないかと思います。 Xcode5で新規クラスファイルを作成するには、通常 「iOS」-「Cocoa Touch」-「Objective-C class」 を選択しますが、 Xcode6でそれに相当するのは 「iOS」-「Source」-「Cocoa Touch Class」 です。 「iOS」-「Source」-「Objective-C File」 というのもありますが、そちらを選ぶと カテゴリ形式、プロトコル形式、クラス拡張形式のソースのひな型を 作ってしまいますので、誤ってそちらを選んで、意図せずカテゴリを 使ったソースを作ってしまったということはないでしょうか? もしそうなら作り直した方がよいと思います。 > >IB で TableView のクラスを TouchableTableView に変更 > という部分が、どこを指しているのか分からず、どうしたら良いのか分かりません。 > どこのTableViewのクラスを、TouchableTableViewに変更したら良いのでしょうか? ちゃんと「UITableView のサブクラス TouchableTableView を作成」できたなら UITableViewController内のTable ViewのCustom ClassにTouchableTableViewを 指定できるようになるはずです。 それだけでもともとやりたかったことができるかどうかは、私自身検証したことなくて 今は検証する時間もないのでわかりませんが、少なくともそのサイトで書いているのは カテゴリではなく、通常の継承を使った方法です。

ifonesapp
質問者

お礼

回答ありがとうございます! お察しの通り、カテゴリを使う意図はありませんでした・・すみません。 クラスファイルの作り方で迷い、カテゴリを選択してみた時に今までのようにヘッダファイルと実行ファイルが作成されたので、クラスの記述方法が変わったんだくらいに思ってました。 ファイルの新規作成方法まで丁寧に教えていただいてとても助かります。 教えていただいた通りにコードを記述し、サブクラスを指定したところ、セルをタップした時でもキーボードを引っ込められるように出来ました! ただビューを移動するセルをタップすると、キーボードを引っ込めながらビューを移動してしまうので、 NSNotificationとUIKeyboardDidShowNotificationでキーボード表示をBOOL値を管理してキーボードが表示されている時は、キーボード外全体はキーボードを閉じるだけの処理というもともと考えていた動きを実装する事ができました! あとはスクロール時の処理も組み込んでみたいと思います。 回答ありがとうございました!

その他の回答 (1)

  • harawo
  • ベストアンサー率58% (3742/6450)
回答No.1

その「TouchableTableView」というのは、Objective-Cのひとつの概念「カテゴリ」を利用したものです。カテゴリはほかのオブジェクト指向の言語にはみられない、Objective-C独自のもので、ひじょうに強力で、それゆえ扱いに気をつけたほうがいいものです。カテゴリは、クラスのメソッドを上書きします。既存のFoundation、UIKitフレームワークのクラスも書き換え可能なので、使い方を間違えると、こわいことになりますね。 UIScrollViewは、タッチイベント(touchesBegan:、touchesMoved:、touchesEnded:など)を上位のResponderに渡さない性質を持ちます。UITableViewは、UiScrollViewのサブクラスなので、おなじ性質です。これをカテゴリを使って、タッチイベントを上位のResponderに渡すようにします。 なお、カテゴリは別ファイルとして作成する必要はなく、プログラマの都合のいい場所に記述することができます。今回の例では、UIViewControllerサブクラスの実装ファイル(例:ViewController.m)に記述するといいでしょう。 #import "ViewController.h" @interface UITableView (Touchable) // カッコ内は、カテゴリ名。任意です。好きな名称をつければいいです。 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; @end @implementation UITableView (Touchable) - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.nextResponder touchesBegan: touches withEvent: event]; // タッチイベントを、つぎのResponderにそのまま渡す。 } @end @implementation ViewController ~中略 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.view endEditing:YES]; // タッチイベントを受け取ったら、キーボードを下げる。 } ~以下略 @end 「super touchesBegan: ~」は、記述しませんでしたが、必要かもしれません。いまのところ私には判断できません。 さて、以上のカテゴリを追加しても、キーボードは下がらないと思います。それは、じつはUITableViewCellもタッチイベントを止めてしまうからなのです。 UITableViewCellのサブクラスを実装していると思います。それにも、 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event { [self.nextResponder touchesBegan: touches withEvent: event]; // タッチイベントを、つぎのResponderにそのまま渡す。 } を実装してください。そうすれば、テキストフィールド以外のタッチで、キーボードが下がるようになります。 なお、UIScrollViewのプロパティ「keyboardDismissMode」があって、この値を「UIScrollViewKeyboardDismissModeOnDrag」にすると、スクロールビュー(テーブルビュー)をスクロールすることで、キーボードを下げることができます。これはStoryboard上でも設定できるので、「それでもいいか」と思ったら、こちらを使うほうがかんたんです。

ifonesapp
質問者

お礼

回答ありがとうございます! 意図せずカテゴリというものを使っていたようで、カテゴリというものを知りませんでした;すみません。 カテゴリというものについて少し調べてみましたが、既存のクラスにメソッドを追加できるそうですね。 確かに、やりようにやっては何でもできてしまいそうな印象で、それ故に扱いが難しそうです。 今回はカテゴリを使用せずに処理できるようなので使用せずにプログラムを組んでみようと思いますが、今後カテゴリを使う機会があった時、記述方法を参考にさせていただきます! タップした時だけキーボードを閉じる仕様を考えていましたが、 スクロールした時は閉じないという仕様だと、動作がわずらわしいなと思ったので、 UIScrollViewのプロパティ「keyboardDismissMode」を使ってキーボードを閉じる処理にも挑戦してみようと思います ありがとうございました

関連するQ&A

  • iPhone SDKに関して

    iPhone SDKに関して 初めてこちらを利用させてもらいます。 よろしくお願いします。 画面上部でドラッグをしている間座標を取得して、 画面下部のTableViewに軌跡の座標を出力したいと考えているのですが、うまくいきません。 (ソースの一部です) [hファイル] IBOutlet UITableView *table; [mファイル] //座標取得 -(void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { CGPoint locationPoint = [[touches anyObject] locationInView:self]; CGPoint previousLocationPoint = [[touches anyObject] previousLocationInView:self]; 最初はNSMutableArrayにlocationPointを格納して、 テーブルに反映できたらと思っていたのですが、型が一致しないのかエラーがでます。 ドラッグされたときに、リアルタイムで画面下部にあるテーブルに追加して表示したいのですが、こういうことは可能でしょうか? よろしくお願いいたします。

  • Swiftについて教えてください

    Swiftで分からないことがあり、投稿させていただきました。UIImageViewにタッチの機能をつけてlet stamp = Timage()で呼び出しているサンプルを参考にタッチをピンチジェスチャーに変更してlet stamp = Pimage()で呼び出すと Missing argument for parameter 'coder' in callというエラーがでます。調べて呼び出し文で引数が足りない。ということが分かったのですが、どのようにして呼び出したらいいのかわかりません。Swift初心者なので、具体的に教えていただければありがたいです。よろしくお願いします。 タッチ//////////////////////////////////////////////////////////////////////////////////////////////////////////// import UIKit class Timage: UIImageView { override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) { self.superview?.bringSubviewToFront(self) } override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) { let touch = touches.first as! UITouch let dx = touch.locationInView(self.superview).x - touch.previousLocationInView(self.superview).x let dy = touch.locationInView(self.superview).y - touch.previousLocationInView(self.superview).y self.center = CGPointMake(self.center.x + dx, self.center.y + dy) } } ピンチジェスチャー/////////////////////////////////////////////////////////////////////////////////////////////////////////////// import UIKit class Pimage: UIImageView { var gestureEnabled = true private var beforePoint = CGPointMake(0.0, 0.0) private var currentScale:CGFloat = 1.0 required init(coder aDecoder: NSCoder) { super.init(coder: aDecoder) self.userInteractionEnabled = true let pinchGesture = UIPinchGestureRecognizer(target: self, action: "Pinch") self.addGestureRecognizer(pinchGesture) } }

    • ベストアンサー
    • Swift
  • UITableViewDataSourceエラー

    xcode 7.1 (7B91b)を使用しています。 SwiftでViewControllerにUItableViewを追加した時に、 クラスの定義でdoes not conform to protocol ‘UITableViewDataSource’エラーが発生します。 サイトで調べた結果2つのメソッドを記載することとして、下記を追加しましたが、 一向にエラーが取れません。他なにか要因がありますでしょうか?ご教示のほどよろしくお願いします。 func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ } func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{ return UITableViewCell() } 全体 class ShopListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource{ } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int{ } func tableView(tableView: UITableView,cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell{ return UITableViewCell() }

    • ベストアンサー
    • Swift
  • TableViewでセル名が表示されない

    こんにちは。 iPhoneアプリの勉強している者ですが、TableViewControllerを使いセルごとに名前を表示させようとしているのですが、タイトルのみが表示されて肝心のセル名が表示されません。 appDelegate.mにてUINavigationControllerのインスタンス及びUITableViewControllerのインスタンスを作成してUITableViewControllerのインスタンスをrootViewControllerに設定しTableViewを表示させようとしています。 プログラムの記述のどこが間違っているのか教えて頂けると大変助かります。 以下UITableViewControllerのinterfaceセクションとimplementationセクションを掲載しますので、ご指摘お願いします。 #import <UIKit/UIKit.h> @interface SampleForSimpleTable : UITableViewController { NSArray *itemes; } @end @implementation SampleForSimpleTable - (id)initWithStyle:(UITableViewStyle)style { self = [super initWithStyle:style]; if (self) { // Custom initialization } return self; } - (void)viewDidLoad { [super viewDidLoad]; self.title = @"SimpleTable"; itemes = [[NSArray alloc] initWithObjects:@"ITME 1", @"ITEM 2", @"ITEM 3", @"ITEM 4", @"ITEM 5", @"ITEM 6", nil]; } #pragma mark - Table view data source - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView { #warning Potentially incomplete method implementation. // Return the number of sections. return 0; } - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section { #warning Incomplete method implementation. // Return the number of rows in the section. return [itemes count]; } - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { static NSString *identifier = @"default-cell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier ]; if (!cell){ cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier]; } cell.textLabel.text = [itemes objectAtIndex:indexPath.row]; // Configure the cell... return cell; }

  • Obj-Cで生成元のViewへ文字列を表示させたい

    iOS SDK(Objective-C)を勉強中なのですが、あるView(TestViewController.view)から別のView(ButtonViewController.view)を表示し、表示したViewにあるボタンを押した時に、生成元のView(TestViewController.view)のラベル(test_label)に文字列を表示したいのですが、色々やってみたものの上手く行かず、どの用に実装するのが正しいのか行き詰まってしまいました。どなたか手助けよろしくお願いします。 //*** TestViewController.h #import <UIKit/UIKit.h> #import "ButtonViewController.h" @interface TestViewController : UIViewController { IBOutlet UILabel *test_label; ButtonViewController *buttonViewController; } - (IBAction) pushButton:(id)sender; @end //*** TestViewController.m #import "TestViewController.h" @implementation TestViewController - (void)viewDidLoad { [super viewDidLoad]; test_label.text = @"Push Button"; } //ButtonViewを表示 - (IBAction) pushButton:(id)sender { buttonViewController = [[ButtonViewController alloc] initWithNibName:@"ButtonView" bundle:nil]; [self.view addSubview:buttonViewController.view]; } - (void)dealloc { [buttonViewController release]; [super dealloc]; } @end //*** ButtonViewController.h #import <UIKit/UIKit.h> @interface ButtonViewController : UIViewController { } - (IBAction) pushButtonSub:(id)sender; @end //*** ButtonViewController.m #import "ButtonViewController.h" @implementation ButtonViewController - (void)viewDidLoad { [super viewDidLoad]; } - (IBAction) pushButtonSub:(id)sender { // ??? ← このボタンを押したときにtest_labelへ表示させたい } - (void)dealloc { [super dealloc]; } @end

  • iPhoneアプリ 画面遷移での変数受け渡し

    現在、iPhoneアプリの開発を勉強している者です。 少し詰まってしまった箇所があるので、ご教授いただけると幸いです。 TableViewController間での変数の受け渡しを実装しており、1枚目のTableViewでタップされたセルのテキストを2枚目に渡す、というコードを書きました。 受け渡しに関するコードは以下の部分です。 -(void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { [self performSegueWithIdentifier:@"mySegue" sender:self]; } -(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { if ([[segue identifier] isEqualToString:@"mySegue"]) { SecondTableViewController *viewCon = segue.destinationViewController; NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; UITableViewCell *cell = [self.tableView cellForRowAtIndexPath:indexPath]; viewCon.myValue = cell.textLabel.text; } } これ自体は問題なく動いてくれたのですが、SecondTableViewControllerにNavigationControllerをEmbed Inしたところ、1枚目でセルをタップした際にエラーが出てアプリが止まってしまうようになりました。 BreakPointを設けてみると上記コードのviewCon.myValue = cell.textLabel.text;のところで止まっており、unrecognized selector sent to instanceというエラーが吐き出されていました。 同様のエラーが出た例などを調べてみたのですが自力では解決できず・・・、どなたか原因を指摘して頂けるとありがたいです。 よろしくお願いします。

  • Objective-c  画面遷移について

    毎回、初歩的な質問内容で申し訳ありませんが、困っているので教えて下さい。 「FirstViewController」はUIScrollViewでスクロールさせている画面で、タッチイベントを取得する為に UIScrollView のサブクラスを作っています。 そこでタッチすると、「SecondViewController」へ固定の画面で遷移するようにしたいのですが、「SecondViewController」の画面もそのままスクロール画面で表示されてしまいます。。。 「SecondViewController」をスクロールさせず、位置x=0 y=0で表示させるにはどうしたら良いのでしょうか? 詳しく教えて頂けると、大変助かります。。 ↓FirstViewController.h↓ @interface FirstViewController : UIViewController{   MyScrollview *controller; IBOutlet UIScrollView *scrollView; } ↓FirstViewController.m↓ - (void)viewDidLoad { controller = [[ MyScrollview alloc] initWithFrame:CGRectMake(0, 0, 1100, 1100)]; self.view = controller; [super viewDidLoad]; [super loadView]; scrollView.contentSize = controller.frame.size; [scrollView addSubview:controller]; } ↓MyScrollview .h↓ @interface MyScrollview : UIView<UIScrollViewDelegate>{ } @end ↓MyScrollview .m↓ -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{ SecondViewController *svc; svc = [[SecondViewController alloc] init]; [self addSubview:[svc view]]; }

  • xcodeでのiOS開発で、コードで画面遷移する時

    xcodeでのiOS開発で、コードで画面遷移する方法について質問です。 テーブルビューで、セルをタップすると呼び出されるメソッドで コードで画面遷移をさせようとしています。 しかし、画面遷移をすると遷移先の画面が真っ黒になってしまいます。 どうすればなおりますか? - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { NextViewController *nvc = [[NextViewController alloc] init]; nvc.hidesBottomBarWhenPushed = YES; [self.navigationController pushViewController:nvc animated:YES]; } ちなみに、遷移先のクラスはインポートしてあります。 StoryBoardは使っていません。

  • Objective-c 画面遷移について

    やはり、画面遷移ではまっていますので、助けて下さい。 navigationControllerで作成しています。 (内容は以前に質問させて頂いているものとほぼ同じですが、) 「FirstViewController」はUIScrollViewでスクロールさせている画面で、タッチイベントを取得する為に UIScrollView のサブクラスを作っています。 そこでタッチすると、「SecondViewController」へ遷移するようにしたいのですが、下記のように単純にpushViewControllerを使ってサブクラスへ書くと、「Property 'navigationContoroller' not found on object of type 'MyScrollview'」というエラーが表示されてしまいます。 このクラスへ書いたらいけないというのは、なんとなく分かりますが、かと言って何からどう手をつければ良いのか分かりません…… 問題なく画面遷移させるにはどうしたらよいのでしょうか。 詳しく教えて頂けると、大変助かります。。 ↓FirstViewController.h↓ @interface FirstViewController : UIViewController{   MyScrollview *controller; IBOutlet UIScrollView *scrollView; } ↓FirstViewController.m↓ - (void)viewDidLoad { controller = [[ MyScrollview alloc] initWithFrame:CGRectMake(0, 0, 1100, 1100)]; self.view = controller; [super viewDidLoad]; [super loadView]; scrollView.contentSize = controller.frame.size; [scrollView addSubview:controller]; } ↓MyScrollview .h↓ @interface MyScrollview : UIView<UIScrollViewDelegate>{ } @end ↓MyScrollview .m↓ -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent*)event{ ~省略~ SecondViewController *mvc = [[[SecondViewController alloc] initWithNibName:nil bundle:nil] autorelease]; [self.navigationController pushViewController:mvc animated:YES]; }

  • iPhoneアプリ とあるプログラムの質問です。

    色々調べたり、書き換えてみたりしたのですが、原因がいまいちよくわかりません。お手上げなので質問させていただきます。 Nabigation-Basedのアプリ製作の中で、テーブル表示から下層画面を選択して、画面遷移をするためのプログラムをRootViewController.mに書いているのですが、シミュレータで実行すると [self.navigationController pushViewController:detailViewController animated:YES]; で、Thread1:Program received signal:"SIGABRT"の表示がでます。(iOS 4.1です) 考えられる原因は何があるでしょうか。ヒントでも構いませんのでよろしくお願い致します。前後の文は下記です。 - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath { indexPath.section == 0 ; if( indexPath.row == 0 ) { cal_1_UIViewController *detailViewController = [[cal_1_UIViewController alloc] initWithNibName:@"cal_1_UIViewController" bundle:nil]; ☆☆☆[self.navigationController pushViewController:detailViewController animated:YES];☆☆☆→エラーがでます。 [detailViewController release]; }else if( indexPath.row == 1 ) { cal2_UIViewController *detailViewController = [[cal2_UIViewController alloc] initWithNibName:@"cal2_UIViewController" bundle:nil]; [self.navigationController pushViewController:detailViewController animated:YES]; [detailViewController release]; }else if( indexPath.row == 2 ) { cal3_UIViewController *detailViewController = [[cal3_UIViewController alloc] initWithNibName:@"cal3_UIViewController" bundle:nil]; [self.navigationController pushViewController:detailViewController animated:YES]; [detailViewController release]; }

専門家に質問してみよう