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

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

ただいまの
回答率

87.79%

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

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,934

score 15

アプリのボタンを押すと他のストーリボードを呼び出すようにコードを書いたのですが、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)
        }
        



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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • Stripe

    2015/07/25 22:27

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

    キャンセル

  • Stripe

    2015/07/26 02:25

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

    キャンセル

  • takaratony

    2015/07/26 13: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.
    }


    }

    最後に技術的な討論が出来るってカッコいいことだと思うんだ。

    キャンセル

回答 1

checkベストアンサー

-1

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

dynamic func callemail(sender: UIButton) {

}

dynamic func callphoto(sender: UIButton) {

}

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

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/07/26 02: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")
    }
    }

    キャンセル

  • 2015/07/26 02:12

    IBOutletを使っても結果は同じです。

    純粋なSwiftクラスのメソッドはObjective-Cから呼べません。呼ぶためには@objcを付ける必要があります。
    しかし、NSObjectクラスを継承したSwiftクラスは自動的にObjective-Cとの互換機能が働くため、@objcとか付けなくてもObjective-CからSwiftメソッドを呼べます。
    って、ちゃんとアップルのドキュメントに書いてあるんですけどね〜。

    ViewControllerクラスも継承を辿っていけば、NSObjectに到達するため、SwiftコンパイラのObjective-C互換機能が働きます。

    今回の質問者のコードには、その辺が明記されていませんが、self.view.addSubview()とかやってますから、UIViewControllerのサブクラスであることは間違いないと思いますが。

    キャンセル

  • 2015/07/26 02: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ランタイムは嫌というほど触ってきたので話は通じてますよ)

    キャンセル

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

  • ただいまの回答率 87.79%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る