50445


環境:Mac/High Sierra 10.13.6/swift4.2/xcode 10.0

下記コードでUIButtonをaddSubviewした時に、うまく動きません。  
ViewController上でUIButtonを直接定義するとうまく動くことが確認できました。  
また、BulbSwitchButtonクラス内のイニシャライザでViewControllerを引数で指定して、プロパティに追加(weak var
vc:ViewController)すればうまくいくことも確認しました。  
ただ、下記コードのようにViewController上で、他クラスのUIButtonプロパティをaddSubview()した時に、クリックしてもBulbSwitchButtonクラスのaction()が動作しない理由がわかりませんでした。

ViewController.swift

[code]

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            super.viewDidLoad()
            // Do any additional setup after loading the view, typically from a nib.
    
            var bulb = Bulb(id: 1, state: true)
            var bulbSwitch = BulbSwitchButton(bulb: bulb)
            bulbSwitch.button.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width/7, height: self.view.bounds.height/7)
            bulbSwitch.button.center = self.view.center
            self.view.addSubview(bulbSwitch.button)
    
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        @objc func testAction(_ sender:UIButton){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
    
    }
    
[/code]

Bulb.swift

[code]

    import Foundation
    import UIKit
    
    class Bulb{
        let id:Int
        var state:Bool = false
        var image:UIImage?
        var imageView:UIImageView = UIImageView()
        var onFilename:String = "on"
        var offFilename:String = "off"
    
        init(id:Int){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            self.id=id
            self.setImage()
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        init(id:Int,state:Bool){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            self.state=state
            self.id=id
            self.setImage()
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        func setImage(){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            if self.state{
                self.image = UIImage(named: self.onFilename)
                self.imageView.image = self.image
            }else{
                self.image = UIImage(named: self.offFilename)
                self.imageView.image = self.image
            }
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
    
        func on(){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            self.state = true
            setImage()
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        func off(){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            self.state = false
            setImage()
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        func setOnOff(state:Bool){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            if state{
                self.on()
            }else{
                self.off()
            }
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    }
    
    
    class BulbSwitchButton{
        let bulb:Bulb
        let id:Int
        var filename:String = "back"
        let button:UIButton = UIButton()
    
        init(bulb:Bulb){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            self.bulb = bulb
            self.id = self.bulb.id
            self.button.addTarget(self, action: #selector(action(_:)), for: .touchUpInside)
            self.button.setImage(UIImage(named: filename), for: .normal)
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    
        @objc func action(_ sender:UIButton){
            Logger.debug(Logger.Message.START_FUNCTION.rawValue)
            Logger.debug(Logger.Message.END_FUNCTION.rawValue)
        }
    }
    
[/code]


CC BY-SA 4.0
2018-11-19T08:44:26.487
50440
2018-11-19T11:57:49.463
25745
question
swift xcode swift4
UIButtonのaddTargetがうまくいかない


いったいどのようなコードの場合に期待の動作をすることを確かめられたのか、記載の内容だけではよくわからないのですが、掲載されたコードが動かない原因ははっきりしているので、今回は特に問題はないでしょう。

根本的な原因は、`BulbSwitchButton`のインスタンスを保持している変数`bulbSwitch`がメソッド`viewDidLoad()`のローカル変数であるためです。作成された`BulbSwitchButton`のインスタンスは`bulbSwitch`にしか強参照を保持されていないので、`viewDidLoad()`の終了とともに解放されてしまいます。

`BulbSwitchButton`のイニシャライザの中で`self`(==
`viewDidLoad()`の`bulbSwitch`)を`addTarget`していますが、UIControlはtargetの弱参照しか保持せず、targetがどこかで解放されてしまうと黙ってactionを無視してしまいます。

* * *

と言うわけで手っ取り早く対応するには、`bulbSwitch`をインスタンス変数にして、`viewDidLoad()`が終了しても、`BulbSwitchButton`のインスタンスが解放されないようにしてしまえば良いでしょう。

[code]

    var bulbSwitch: BulbSwitchButton!
    
    override func viewDidLoad() {
        Logger.debug(Logger.Message.START_FUNCTION.rawValue)
        super.viewDidLoad()
    
        let bulb = Bulb(id: 1, state: true)
        bulbSwitch = BulbSwitchButton(bulb: bulb)
        bulbSwitch.button.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width/7, height: self.view.bounds.height/7)
        bulbSwitch.button.center = self.view.center
        self.view.addSubview(bulbSwitch.button)
    
        Logger.debug(Logger.Message.END_FUNCTION.rawValue)
    }
    
[/code]

ただ、少し使い方を誤るとtargetに設定したはずのインスタンスが解放されてしまうと言うのは、現在のクラス分割があまりうまく出来ていないせいだと思った方が良いでしょう。(他にも直したいところはあれこれありますが、まだ発展途上のコードのようですし、今回は置いておきます。)


CC BY-SA 4.0
2018-11-19T11:57:49.463
50445
2018-11-19T11:57:49.463
13972
50440
answer