

UIKitのUIMenuControllerの使い方がわからないため、以下のURL先に書いてあるコードをViewController.swiftにコピペしたのですが、エラーが出てしまいました。どうすればいいでしょうか？

<https://sites.google.com/a/gclue.jp/swift-docs/ni-
yinki100-ios/uikit/uimenucontrollernimenuitemwo-zhui-jia>

[code]

    //
    //  ViewController.swift
    //  UIKit033
    //
    
    import UIKit
    
    class ViewController: UIViewController, UITextFieldDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // 背景を青色に設定.
            self.view.backgroundColor = UIColor.cyanColor() //[エラー]Cannot call value of non-function type 'UIColor'Remove '()'
            
            // TextField作成.
            let myTextField: UITextField = UITextField(frame: CGRectMake(0, 0, 200, 30))
            myTextField.text = "Hello Swift!"
            myTextField.delegate = self
            myTextField.borderStyle = UITextBorderStyle.RoundedRect
            myTextField.layer.position = CGPointMake(self.view.frame.width/2, 100)
            
            // TextFieldをviewに追加.
            self.view.addSubview(myTextField)
            
            // MenuController生成.
            let myMenuController: UIMenuController = UIMenuController.sharedMenuController()
            
            // MenuControllerを表示.
            myMenuController.menuVisible = true
            
            // 矢印の向きを下に設定.
            myMenuController.arrowDirection = UIMenuControllerArrowDirection.Down
            
            // rect、viewを設定.
            myMenuController.setTargetRect(CGRectZero, inView: self.view)
            
            
            // MenuItem生成.
            let myMenuItem_1: UIMenuItem = UIMenuItem(title: "Menu1", action: "onMenu1:")
            let myMenuItem_2: UIMenuItem = UIMenuItem(title: "Menu2", action: "onMenu2:")
            let myMenuItem_3: UIMenuItem = UIMenuItem(title: "Menu3", action: "onMenu3:")
            
            // MenuItemを配列に格納.
            let myMenuItems: NSArray = [myMenuItem_1, myMenuItem_2, myMenuItem_3]
            
            // MenuControllerにMenuItemを追加.
            myMenuController.menuItems = myMenuItems as [AnyObject]
        }
        
        /*
        UITextFieldが編集開始された直後に呼ばれる.
        */
        func textFieldDidBeginEditing(textField: UITextField) {
            println("textFieldDidBeginEditing:" + textField.text)
        }
        
        /*
        UITextFieldが編集終了する直前に呼ばれる.
        */
        func textFieldShouldEndEditing(textField: UITextField) -> Bool {
            println("textFieldShouldEndEditing:" + textField.text)
            return true
        }
        
        /*
        作成したMenuItemが表示されるようにする.
        */
        override func canPerformAction(action: Selector, withSender sender: AnyObject!) -> Bool {
            if action == "onMenu1:" || action == "onMenu2:" || action == "Menu3:" {
                return true
            }
            return false
        }
        
        /*
        作成したMenuItemが押された際に呼び出される.
        */
        internal func onMenu1(sender: UIMenuItem) {
            println("onMenu1")
        }
        
        internal func onMenu2(sender: UIMenuItem) {
            println("onMenu2")
        }
        
        internal func onMenu3(sender: UIMenuItem) {
            println("onMenu3")
        }
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    }
    
[/code]


CC BY-SA 4.0
2021-04-08T18:45:00.850
75171
2021-04-09T09:23:44.857
2021-04-09T09:23:44.857
3060
43775
question
swift xcode swift3 uikit
UIMenuControllerの使い方を教えてください


参考にされた記事は超古いSwift1の時代に書かれたもののようです。(コメントに書いたSwift2よりさらに古かったようです。)Swift言語はその後Swift4までの間に大きく変化しているので、Swift初心者ではそれらの変化にご自身で完全に対応するのは極めて難しいだろうと思います。

ただ、いくつかの変化についてはXcodeが修正案付きのヒントを出してくれます。それらを鵜呑みにすると痛い目に遭うことも多いのですが、今回のエラーについては、Xcode
12.4の出してくる修正案は全部そのまま取り入れても問題無いようです。結果コードは以下のようになります。

(`Fix`で修正を受け入れた後、さらに修正案が出てくることもありますが、そこでまた`Fix`で修正を取り込みます。)

[code]

    import UIKit
    
    class ViewController: UIViewController, UITextFieldDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // 背景を青色に設定.
            self.view.backgroundColor = UIColor.cyan //<-
            
            // TextField作成.
            let myTextField: UITextField = UITextField(frame: CGRectMake(0, 0, 200, 30)) //x
            myTextField.text = "Hello Swift!"
            myTextField.delegate = self
            myTextField.borderStyle = UITextField.BorderStyle.roundedRect //<-
            myTextField.layer.position = CGPointMake(self.view.frame.width/2, 100) //x
            
            // TextFieldをviewに追加.
            self.view.addSubview(myTextField)
            
            // MenuController生成.
            let myMenuController: UIMenuController = UIMenuController.shared //<-
            
            // MenuControllerを表示.
            myMenuController.isMenuVisible = true //<-
            
            // 矢印の向きを下に設定.
            myMenuController.arrowDirection = UIMenuController.ArrowDirection.down //<-
            
            // rect、viewを設定.
            myMenuController.setTargetRect(CGRectZero, in: self.view) //<-x
            
            
            // MenuItem生成.
            let myMenuItem_1: UIMenuItem = UIMenuItem(title: "Menu1", action: "onMenu1:")
            let myMenuItem_2: UIMenuItem = UIMenuItem(title: "Menu2", action: "onMenu2:")
            let myMenuItem_3: UIMenuItem = UIMenuItem(title: "Menu3", action: "onMenu3:")
            
            // MenuItemを配列に格納.
            let myMenuItems: NSArray = [myMenuItem_1, myMenuItem_2, myMenuItem_3]
            
            // MenuControllerにMenuItemを追加.
            myMenuController.menuItems = myMenuItems as [AnyObject] //x
        }
        
        /*
         UITextFieldが編集開始された直後に呼ばれる.
         */
        func textFieldDidBeginEditing(textField: UITextField) {
            println("textFieldDidBeginEditing:" + textField.text) //x
        }
        
        /*
         UITextFieldが編集終了する直前に呼ばれる.
         */
        func textFieldShouldEndEditing(textField: UITextField) -> Bool {
            println("textFieldShouldEndEditing:" + textField.text) //x
            return true
        }
        
        /*
         作成したMenuItemが表示されるようにする.
         */
        override func canPerformAction(action: Selector, withSender sender: AnyObject!) -> Bool { //x
            if action == "onMenu1:" || action == "onMenu2:" || action == "Menu3:" {
                return true
            }
            return false
        }
        
        /*
         作成したMenuItemが押された際に呼び出される.
         */
        internal func onMenu1(sender: UIMenuItem) {
            println("onMenu1") //x
        }
        
        internal func onMenu2(sender: UIMenuItem) {
            println("onMenu2") //x
        }
        
        internal func onMenu3(sender: UIMenuItem) {
            println("onMenu3") //x
        }
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    }
    
[/code]

警告まで入れるとさらに長大になってしまうのでエラーだけに絞りますが、およそ以下のような点がエラーとして残ってしまいます。

  1. `CGRectMake`, `CGPointMake`を使っているところが`'CGRectMake' is unavailable in Swift`のようなエラーになる

  2. `CGRectZero`が`'CGRectZero' is unavailable in Swift`のエラーになる

  3. `menuItems =`の代入が`Cannot assign value of type '[AnyObject]' to type '[UIMenuItem]?'`のエラーになる

  4. `println`のところが`Cannot find 'println' in scope`のエラーになる

  5. `override func canPerformAction`のところが、`Method does not override any method from its superclass`のエラーになる

* * *

以下、解決方法を示しますが、古いSwiftのコードを最新のXcodeで使う場合、これらの解決方法を理解して、個別に必要に応じて取り込まないといけません。ご自分には難しいと思われる場合には、もっと時間をかけて新しい記事を見つけてください。

  1. -> `CGRectMake`, `CGPointMake`の代わりに`CGRect`, `CGPoint`のコンストラクタを直接呼び出す

`CGRectMake(0, 0, 200, 30)`なら、`CGRect(x: 0, y: 0, width: 200, height:
30)`に書き換えてやらないといけません。

  2. -> グローバル定数`CGRectZero`の代わりにクラス定数`CGRect.zero`を使う

  3. -> `NSArray`型の中間変数なんて使わない

  4. -> `println`を`print`に置き換える

  5. -> `canPerformAction(action:withSender:)`を`canPerformAction(_:withSender:)`に書き換える

* * *

とここまで書いたところで、無視できない警告があることに気づきました。

  6. `No method declared with Objective-C selector 'onMenu1:'`の警告が出ている場所がある

残念ながら、この警告は素直にXcodeの指示に従ってはいけない部分です。

  7. `textFieldDidBeginEditing`, `textFieldShouldEndEditing`に`Instance method 'textFieldDidBeginEditing(textField:)' nearly matches optional requirement 'textFieldDidBeginEditing' of protocol 'UITextFieldDelegate'`のような警告が出る

以上の警告は適切に修正しないと所望の動作になりません。

  6. -> セレクタで参照されるメソッドに`@objc`を付け、参照する部分は`#selector(...)`構文を使う

  7. -> Xcodeの候補のうち最初(`Rename to '...' to satisfy this requirement`)を使う

* * *

上記の修正を全部取り入れたコードは以下のようになります。

[code]

    import UIKit
    
    class ViewController: UIViewController, UITextFieldDelegate {
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            // 背景を青色に設定.
            self.view.backgroundColor = UIColor.cyan //<-
            
            // TextField作成.
            let myTextField: UITextField = UITextField(frame: CGRect(x: 0, y: 0, width: 200, height: 30)) //#1
            myTextField.text = "Hello Swift!"
            myTextField.delegate = self
            myTextField.borderStyle = UITextField.BorderStyle.roundedRect //<-
            myTextField.layer.position = CGPoint(x: self.view.frame.width/2, y: 100) //#1
            
            // TextFieldをviewに追加.
            self.view.addSubview(myTextField)
            
            // MenuController生成.
            let myMenuController: UIMenuController = UIMenuController.shared //<-
            
            // MenuControllerを表示.
            myMenuController.isMenuVisible = true //<-
            
            // 矢印の向きを下に設定.
            myMenuController.arrowDirection = UIMenuController.ArrowDirection.down //<-
            
            // rect、viewを設定.
            myMenuController.setTargetRect(.zero, in: self.view) //#2
            
            
            // MenuItem生成.
            let myMenuItem_1: UIMenuItem = UIMenuItem(title: "Menu1", action: #selector(onMenu1)) //#6
            let myMenuItem_2: UIMenuItem = UIMenuItem(title: "Menu2", action: #selector(onMenu2)) //#6
            let myMenuItem_3: UIMenuItem = UIMenuItem(title: "Menu3", action: #selector(onMenu3)) //#6
            
            // MenuControllerにMenuItemを追加.
            myMenuController.menuItems = [myMenuItem_1, myMenuItem_2, myMenuItem_3] //#3
        }
        
        /*
         UITextFieldが編集開始された直後に呼ばれる.
         */
        func textFieldDidBeginEditing(_ textField: UITextField) { //#7
            print("textFieldDidBeginEditing:" + textField.text!) //#4
        }
        
        /*
         UITextFieldが編集終了する直前に呼ばれる.
         */
        func textFieldShouldEndEditing(_ textField: UITextField) -> Bool { //#7
            print("textFieldShouldEndEditing:" + textField.text!) //#4
            return true
        }
        
        /*
         作成したMenuItemが表示されるようにする.
         */
        override func canPerformAction(_ action: Selector, withSender sender: Any?) -> Bool { //#5
            if action == #selector(onMenu1) || action == #selector(onMenu2) || action == #selector(onMenu3) { //#6(記事のコードに間違いあり)
                return true
            }
            return false
        }
        
        /*
         作成したMenuItemが押された際に呼び出される.
         */
        @objc func onMenu1(sender: UIMenuItem) { //#6
            print("onMenu1") //#4
        }
        
        @objc func onMenu2(sender: UIMenuItem) { //#6
            print("onMenu2") //#4
        }
        
        @objc func onMenu3(sender: UIMenuItem) { //#6
            print("onMenu3") //#4
        }
        
        override func didReceiveMemoryWarning() {
            super.didReceiveMemoryWarning()
        }
    }
    
[/code]

古いSwiftの記事を頑張って利用しようと思うと、上記のような修正を

  * Xcodeから表示される修正案は取り込んでも大丈夫なのか
  * どの警告はすぐに対応しないとダメなのか、どの警告はとりあえずは無視しても動くのか
  * Xcodeから修正案が表示されない場合、何が悪いのか、どう修正すべきか

辺りを理解した上で、自分で適用していかないといけません。

ネット上の検索では古い記事ほど上位にくることが多くなるわけですが、適切な記事を見つけられるようにならないと、「調べたい機能の使い方」よりも「古いコードの書き換え方」の学習に時間を取られることになるでしょう。


CC BY-SA 4.0
2021-04-09T00:14:37.867
75172
2021-04-09T00:14:37.867
13972
75171
answer