質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

Q&A

解決済

1回答

2499閲覧

switch文で書いたボタンの機能がきちんと定義できていない件について

takaratony

総合スコア15

Swift

Swiftは、アップルのiOSおよびOS Xのためのプログラミング言語で、Objective-CやObjective-C++と共存することが意図されています

0グッド

0クリップ

投稿2015/07/24 06:13

アプリのボタンを押すと他のストーリボードを呼び出すようにコードを書いたのですが、unrecognized selector sent to instance というエラーが出ます。たぶん、ボタンの操作設定がよろしくないと思い、ググって調べてみたら、似たようなエラーの際に(sender: UIButton)という部分を追加したら治ったと書いてあったのですが、治りません。

優しい誰か教えてください。よろしくお願いします。下に部分的にソースを貼ります。

enum BtnTag: Int { case Zero = 1, One, Two static let allValues = [Zero, One, Two ] } for btag in BtnTag.allValues { var y = CGFloat(60 * btag.rawValue - 1) var btn = UIButton(frame: CGRectMake(0, y, myAppFrameSize.width, 50)) // サイズを設定する. btn.backgroundColor = UIColor.grayColor() // 背景色を設定する. btn.layer.masksToBounds = true // 枠を丸くする. btn.tag = btag.rawValue btn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal) switch btn.tag{ // タイトルを設定する // イベントを追加する case 1: btn.setTitle("写真選択", forState: UIControlState.Normal) btn.addTarget(self, action: "callphoto:", forControlEvents: .TouchUpInside) case 2: btn.setTitle("メール", forState: UIControlState.Normal) btn.addTarget(self, action: "callemail:", forControlEvents: .TouchUpInside) case 3: btn.setTitle("やめる", forState: UIControlState.Normal) btn.addTarget(self, action: "callphoto:", forControlEvents: .TouchUpInside) default: break } self.view.addSubview(btn) } func callemail(sender: UIButton!){ //mail画面に移動 let secondViewController: second = self.storyboard?.instantiateViewControllerWithIdentifier("secondvc") as second

self.presentViewController(secondViewController, animated: true, completion: nil)
}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Stripe

2015/07/25 13:27

エラーメッセージを部分的にじゃなく、全て書いてください。
Stripe

2015/07/25 17:25

あと、ソースコードは、クラス全体を書いてください。
takaratony

2015/07/26 04:40

メールボックスにteratailから大量のメールが送られているのに気付き、今、このサイトをみています。 結論から言うとdynamicを追加して、build通りました。 初め、Only members of classes may be dynamicというエラーが出て少し焦ったのですが・・・__moaiさん、Stripeさんのお話を伺うことができたおかげでなんとなく使っていたUIKitへの理解が少し深まりました。もう少しレベルが上がれば、公式のリファレンスもじっくり目を通したいと思います。議論を巻き起こした落書きみたいなソースコードですが下に貼っておきます。ありがとうございました。 import UIKit class ViewController: UIViewController,UIImagePickerControllerDelegate,UINavigationControllerDelegate { //ios7以前のサイズに対応 func screenSize() -> CGSize { let screenSize = UIScreen.mainScreen().applicationFrame.size if (NSFoundationVersionNumber <= NSFoundationVersionNumber_iOS_7_1) && UIInterfaceOrientationIsLandscape(UIApplication.sharedApplication().statusBarOrientation) { return CGSizeMake(screenSize.height, screenSize.width) } return screenSize } override func viewDidLoad() { super.viewDidLoad() screenSize() // Windowの表示領域すべてのサイズ(pixel). let myAppFrameSize: CGSize = UIScreen.mainScreen().applicationFrame.size let myScale: CGFloat = UIScreen.mainScreen().scale enum BtnTag: Int { case Zero = 1, One, Two static let allValues = [Zero, One, Two ] } for btag in BtnTag.allValues { var y = CGFloat(60 * btag.rawValue - 1) var btn = UIButton(frame: CGRectMake(0, y, myAppFrameSize.width, 50)) // サイズを設定する. btn.backgroundColor = UIColor.grayColor() // 背景色を設定する. btn.layer.masksToBounds = true // 枠を丸くする. btn.tag = btag.rawValue btn.setTitleColor(UIColor.whiteColor(), forState: UIControlState.Normal) switch btn.tag{ // タイトルを設定する // イベントを追加する case 1: btn.setTitle("写真選択", forState: UIControlState.Normal) btn.addTarget(self, action: "callphoto:", forControlEvents: .TouchUpInside) case 2: btn.setTitle("メール", forState: UIControlState.Normal) btn.addTarget(self, action: "callemail:", forControlEvents: .TouchUpInside) case 3: btn.setTitle("やめる", forState: UIControlState.Normal) btn.addTarget(self, action: "callphoto:", forControlEvents: .TouchUpInside) default: break } self.view.addSubview(btn) } } func callemail(sender: UIButton!){ //mail画面に移動 let secondViewController: second = self.storyboard?.instantiateViewControllerWithIdentifier("secondvc") as second self.presentViewController(secondViewController, animated: true, completion: nil) } // Do any additional setup after loading the view, typically from a nib. func callphoto(sender: UIButton){ //photoalbumから写真を選択 let photop = UIImagePickerControllerSourceType.PhotoLibrary if UIImagePickerController.isSourceTypeAvailable(photop){ let picker = UIImagePickerController() picker.sourceType = photop picker.delegate = self self.presentViewController(picker, animated: true, completion: nil) } } //画像選択がキャンセルされた時に呼ばれる. func imagePickerControllerDidCancel(picker: UIImagePickerController) { // モーダルビューを閉じる self.dismissViewControllerAnimated(true, completion: nil) } //imageviewに保存でjpgにする func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [NSObject : AnyObject]){ let myAppFrameSize: CGSize = UIScreen.mainScreen().applicationFrame.size var imgi = UIImageView () imgi.frame = CGRectMake(0,0,myAppFrameSize.height,myAppFrameSize.width) let image = info[UIImagePickerControllerOriginalImage] as UIImage self.view.addSubview(imgi) imgi.contentMode = UIViewContentMode.ScaleAspectFit imgi.image = image //画像をNSDataに変換 let data:NSData = UIImageJPEGRepresentation(image,0.99 ) /// 画像ファイルの保存先パスを生成します(ドキュメントフォルダ直下固定)。 var imagePath: String { let doc = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0] as String return doc.stringByAppendingPathComponent("img1.jpg") } //ファイルにデータ書き込み data.writeToFile(imagePath, atomically: true) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. } } 最後に技術的な討論が出来るってカッコいいことだと思うんだ。
guest

回答1

0

ベストアンサー

そうですよね、ちゃんとメソッド用意してるのに動かないってなりますよね・・・
ボタンを押して呼び出したいメソッドの一番最初にdynamicを付けてみてください

Swift

1dynamic func callemail(sender: UIButton) { 2 3} 4 5dynamic func callphoto(sender: UIButton) { 6 7}

dynamicを付けないと動かない理由として、UIKitはObjective-Cのメッセージングでのメソッド呼び出し
がベースとなっているのでSwiftのpureなメソッドに対してセレクタの指定ができないんです

なのでSwiftでUIButtonなどのセレクタが必要なメソッドに対しては、これはObjective-Cのメッセージングで動くメソッドですよっていう目印であるdynamicを付けてあげないとUIButtonはセレクタがどこにあるか探せないで怒ってしまうわけです

投稿2015/07/25 13:03

__moai

総合スコア264

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Stripe

2015/07/25 13:25

SwiftメソッドをObjective-Cメソッドに手動でマッピングするのは、@objcです。 dynamicじゃありません。
__moai

2015/07/25 13:32 編集

>SwiftメソッドをObjective-Cメソッドに手動でマッピングするのは、@objcです。 dynamicじゃありません。 dynamicを付けることでもObjective-Cのランタイム側で解釈してくれますよ、ちゃんと動作を確認してからご指摘願えますか? もしよかったらStripeさんの言う`手動でマッピング`とやらの定義を教えて下さると今後の参考になります
Stripe

2015/07/25 14:08

ちゃんと動作確認はしています。 @objcやdynamicに関して詳しくは、アップルが出しているドキュメントを読んでください。 @objc(hoge:) func boke(sender: AnyObject) { ... } このように書くと、Swift的にはbokeメソッドですが、Objective-Cからはhoge:メソッドになります。 @objcを書かなければ、Objective-Cからはboke:メソッドと解釈されます。 dynamicは、メソッドの実装が差し替えられるようなものについて付ける修飾子です。 今回の件では、書いても書かなくても動作に影響はありません。
__moai

2015/07/25 14:29

> @objcやdynamicに関して詳しくは、アップルが出しているドキュメントを読んでください。 私はStripeさんに@objc/dynamicについて質問してないのですが、どういう意図で読ませたいですか?そこまで書いてくださると勉強になります > ちゃんと動作確認はしています。 では動作確認してUIButtonがセレクタとして指定したメソッドが動いたなら、その結果を書いてくれますか? >今回の件では、書いても書かなくても動作に影響はありません。 あれ??おかしいですね、私の環境だとUIButtonのセレクタとして認識されるようになったのですが、Stripeさんの環境で試されてるSwiftの環境教えてもらっても良いですか? >dynamicは、メソッドの実装が差し替えられるようなものについて付ける修飾子です。 dynamic・@objcの明確な使い分けのソースを提示してください、まさかStripeさんの自身の文面だけで「dynamicは、メソッドの実装が差し替えられるようなものについて付ける修飾子です。」って言われて、納得するかっていうと難しいです、ごめんなさい > func boke(sender: AnyObject) { ... } 実例に出すメソッド名がbokeっていうのは質疑のやり取りのモラルの話になるんですが、非常に失礼なので辞めた方が良いですよ
Stripe

2015/07/25 15:06

「`手動でマッピング`とやらの定義を教えて」とあなたが書いたから、アップルのドキュメントのことや@objcのサンプル書いたのですが? それから、メソッドの呼び出し確認は、単純にコンソールへのログ出力で行っています。 こちらの環境は、Xcode6.4です。 あなたの環境だと、dynamicと書かないとエラーが出るんですか? あと、Objective-Cでは実行時にメソッドの実装を差し替えられるのは知っていますか? (Swiftではできませんが。) つまり、Objective-CからSwiftのメソッドを差し替えることが可能なのです。 しかし、Swiftは自身のメソッドを静的に呼び出しますので、実行時に中身を差し替えられると問題が起こります。 そのメソッドにdynamicを付ければ、Swiftは自身のメソッドをObjective-Cと同様に動的に呼び出すようになります。 ちなみに、foo、bar、hoge、boke、pokeなどの単語は、「特に意味のない適当なシンボル名」として、昔から広く一般的に使われているものです。 boke→ボケ のように日本語として解釈する意味はありません。
__moai

2015/07/25 15:17

>「`手動でマッピング`とやらの定義を教えて」とあなたが書いたから、アップルのドキュメントのことや@objcのサンプル書いたのですが? ではAppleのドキュメントには手動マッピングなる説明があるんですね? 少なくともSwiftのドキュメントに手動マッピングについての説明が見当たらないんですが、どのドキュメントを見ればわかりますか? >>では動作確認してUIButtonがセレクタとして指定したメソッドが動いたなら、その結果を書いてくれますか? >それから、メソッドの呼び出し確認は、単純にコンソールへのログ出力で行っています。 >こちらの環境は、Xcode6.4です。 >あなたの環境だと、dynamicと書かないとエラーが出るんですか? そのときにdynamic修飾子をつけたときの挙動の結果を書いてほしいとお願いしてるのですが、一言でもエラーが出るって言いましたか? ご自身でdynamicを付けても動作に影響が出ないって仰ったんですから、間違えたら間違えましたって素直に言ったらどうでしょうか? >あと、Objective-Cでは実行時にメソッドの実装を差し替えられるのは知っていますか? (Swiftではできませんが。) >つまり、Objective-CからSwiftのメソッドを差し替えることが可能なのです。 >しかし、Swiftは自身のメソッドを静的に呼び出しますので、実行時に中身を差し替えられると問題が起こります。 >そのメソッドにdynamicを付ければ、Swiftは自身のメソッドをObjective-Cと同様に動的に呼び出すようになります。 1つ突っ込んでおくとUIButtonを使ってる時点で動的に中身を差し替えることは可能ですよ、Swiftを使ってる〜っていうのは関係が無いです @objcを使うと静的にセレクタが指定できるなんて知らなかったです!ではそのソースを提示してください >ちなみに、foo、bar、hoge、boke、pokeなどの単語は、「特に意味のない適当なシンボル名」として、昔から広く一般的に使われているものです。 >boke→ボケ のように日本語として解釈する意味はありません。 なるほど、foo, bar, hoge, fugaなどは知ってましたがbokeは知りませんでした。 勉強になります
Stripe

2015/07/25 15:59

「Using Swift with Cocoa and Objective-C」の中の「Exposing Swift Interfaces in Objective-C(27ページあたり)」に、手動でObjective-Cのセレクタを設定する方法が書いています。 まあ、内容は私がさっき書いたサンプルコードのとおりですが。 それから、わたしは「今回は、dynamicは書いても書かなくても動作に影響ない」と言っているんですから、dynamicと書いても書かなくても同じ実行結果になります。 あと、あなたは何かを勘違いしているようですが、「Objective-Cにおけるメソッドの差し替え」っていうのは、「オブジェクトの内容を改変する」というような意味です。 別にUIButtonに設定するセレクタの話をしている訳ではありません。 詳しくは、Objective-CのRuntime APIを参照してください。
__moai

2015/07/25 16:17 編集

>それから、わたしは「今回は、dynamicは書いても書かなくても動作に影響ない」と言っているんですから、dynamicと書いても書かなくても同じ実行結果になります。 コードを動かしてもないのに指摘してますよね? 動作確認もしないで根拠なく指摘してたら信用失いますよ >それから、わたしは「今回は、dynamicは書いても書かなくても動作に影響ない」と言っているんですから、dynamicと書いても書かなくても同じ実行結果になります。 試して実行したコードを載せてください、それをこちらでも実行するので コードは実行結果が全てでしょう、御託並べる前にやることやりましょうよ
Stripe

2015/07/25 16:22

前に書いたとおり、私はちゃんと動作確認をしています。 dynamicと書いたコードと、dynamicと書かなかったコード、それぞれ実行しましたが、その結果は同じでした。 なぜ、同じ結果になったのかについては、先ほどから説明しているとおりです。 逆にあなたの環境ではどうなんですか? dynamicと書いたときと、書かなかったとき、それぞれどのような結果になりましたか?
__moai

2015/07/25 16:24 編集

UIButtonのセレクタとしてメソッドが動作しましたが何か? override func viewDidLoad() { super.viewDidLoad() self.button.addTarget(self, action: "didPushButton", forControlEvents: .TouchUpInside) } dynamic func didPushButton() { print("pushed") }
__moai

2015/07/25 16:26

はい、こちらも見せたのでStripeさんのdynamicをつけて動かなかったコードを見せてください、さあ
__moai

2015/07/25 16:35

結果は目に見えてるのでこれ以上言及は控えますが、ちゃんと条件反射的に指摘をする前にコードを実行する癖をつけましょう 最初から補足として@objcでも動くし、Apple的にはこちらが推薦なんですよって言っておけば済む話だったのに非常にコミュニケーションコストが高くなってお互い不幸ですよね? それだったらば私も、もっと建設的にdynamic、@objcの挙動だったり、使い分けについて話し合おうって思ったんですが動作確認もされてないのは非常にがっかりしました
Stripe

2015/07/25 16:38

import UIKit class ViewController: UIViewController{ override func viewDidLoad() { super.viewDidLoad() let button1 = UIButton(frame: CGRectMake(20, 50, 100, 30)) button1.setTitle("button1", forState: .Normal) button1.setTitleColor(UIColor.blueColor(), forState: .Normal) button1.addTarget(self, action: "button1:", forControlEvents: .TouchUpInside) self.view.addSubview(button1) let button2 = UIButton(frame: CGRectMake(20, 100, 100, 30)) button2.setTitle("button2", forState: .Normal) button2.setTitleColor(UIColor.blueColor(), forState: .Normal) button2.addTarget(self, action: "button2:", forControlEvents: .TouchUpInside) self.view.addSubview(button2) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func button1(sender: UIButton) { println("button1") } dynamic func button2(sender: UIButton) { println("button2") } }
Stripe

2015/07/25 16:43

あなたは、いったい何を勘違いしているのでしょう? 「dynamicを書いて動くコード」だけを提示しても意味ありませんよ。 わたしは、「dynamicは書いても書かなくても動作に影響ない」と言っているのですから。 dynamicと書いても正常に動くし、dynamicと書かなくても正常に動くのです。 ようするに、今回の質問者の質問の件とdynamicとは何の関係もない。と言いたかっただけです。
__moai

2015/07/25 16:52

やっとわかりました、StripeさんはIBOutlet環境で試してないんですね Storyboardやxib経由でUIButtonを扱う場合にはpure Swiftメソッドでは@objc・dynamicを付けないとセレクタとして認識できないんです http://stackoverflow.com/questions/25056278/swift-access-control-with-target-sel ectors >ようするに、今回の質問者の質問の件とdynamicとは何の関係もない。と言いたかっただけです。 それでいくと@objcも全く関係無い範疇になってしまいますね
Stripe

2015/07/25 17:05

import UIKit class ViewController: UIViewController{ @IBOutlet weak var button1:UIButton! @IBOutlet weak var button2:UIButton! override func viewDidLoad() { super.viewDidLoad() button1.addTarget(self, action: "button1:", forControlEvents: .TouchUpInside) button2.addTarget(self, action: "button2:", forControlEvents: .TouchUpInside) } override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } func button1(sender: UIButton) { println("button1") } dynamic func button2(sender: UIButton) { println("button2") } }
Stripe

2015/07/25 17:12

IBOutletを使っても結果は同じです。 純粋なSwiftクラスのメソッドはObjective-Cから呼べません。呼ぶためには@objcを付ける必要があります。 しかし、NSObjectクラスを継承したSwiftクラスは自動的にObjective-Cとの互換機能が働くため、@objcとか付けなくてもObjective-CからSwiftメソッドを呼べます。 って、ちゃんとアップルのドキュメントに書いてあるんですけどね〜。 ViewControllerクラスも継承を辿っていけば、NSObjectに到達するため、SwiftコンパイラのObjective-C互換機能が働きます。 今回の質問者のコードには、その辺が明記されていませんが、self.view.addSubview()とかやってますから、UIViewControllerのサブクラスであることは間違いないと思いますが。
__moai

2015/07/25 17:18 編集

>IBOutletを使っても結果は同じです。 >純粋なSwiftクラスのメソッドはObjective-Cから呼べません。呼ぶためには@objcを付ける必要があります。 >しかし、NSObjectクラスを継承したSwiftクラスは自動的にObjective-Cとの互換機能が働くため、@objcとか付けなくてもObjective-CからSwiftメソッドを呼べます。 >って、ちゃんとアップルのドキュメントに書いてあるんですけどね〜。 細かく言うならpure Swiftメソッドがpublic/internalの場合でないと、ですけどね >今回の質問者のコードには、その辺が明記されていませんが、self.view.addSubview()とかやってますから、UIViewControllerのサブクラスであることは間違いないと思いますが。 とりあえずこれ以上情報が無い中で言い合いしても時間が勿体ないので、質問者さんの返答を待った方がいい気がします dynamicの扱いについてはどこかで語り合いたいところですね (Objective-Cランタイムは嫌というほど触ってきたので話は通じてますよ)
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問