15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【Swift】状態によって変化する値をスマートに実装する

Posted at

はじめに

UIButtonってあるじゃないですか。
タップなどのイベントを取れるUIKitの部品です。
これのラベルを変更させるときによくする間違いで、titleプロパティを直接代入させようとしても変更されないんです。

let btn = UIButton(frame: CGRectZero)
btn.titleLabel?.text = "not change!"

なぜなら、UIButtonにはstateというUIControlStateのプロパティがあって、状態によって値を変えられるからです。直接の代入だと状態の指定ができないので読み取り専用プロパティで代入できないようになっているみたいです。

UIControlStateは通常状態のNormal 、タップ中を表すHighlighted、選択中のSelected などなど。

struct UIControlState : OptionSetType {
    init(rawValue rawValue: UInt)
    static var Normal: UIControlState { get }
    static var Highlighted: UIControlState { get }
    static var Disabled: UIControlState { get }
    static var Selected: UIControlState { get }
    static var Focused: UIControlState { get }
    static var Application: UIControlState { get }
    static var Reserved: UIControlState { get }
}

この状態はこの値ということを設定するためにUIButtonには- setTitle:forState:というメソッドが用意されています。
これでタイトルを変更します。

btn.setTitle("yes change!", forState: .Normal)

ここからが本題です。
いろいろな状態があったとしても、プロパティで設定したほうが便利じゃないかと思ったのです。
UIButtonのようにわざわざメソッドを用意するよりは、プロパティで設定する方がスマートじゃないかと思いました。
なので今回は、状態によって変化する値をプロパティで設定できるようにする方法を作っていきたいと思います。

enumのAssociated Valueを使う

Swiftのenumが使えると思いました。
Swiftのenumの定義の1つにAssociated Valueというものがあり、ある状態とそのときの値を定義することができます。

今回は、カスタムビューを作って、ジェスチャーイベントによって、背景色を変更するコードを作っていきたいと思います。

InteractionStyleViewというカスタムビューを作ります。
カスタムビューにInteractionStyleというenumを定義します。
タップしたとき、ロングタップしたとき、スワイプしたとき、何もしないときの状態と値を以下のように定義します。

InteractionStyleView.swift
class InteractionStyleView: UIView {

    enum InteractionStyle {
        case Tap(UIColor)
        case LongTap(UIColor)
        case Swipe(UIColor)
        case None(UIColor)
    }
}

次に各ジェスチャーに対応するUIColorインスタンスをプロパティで保持します。

InteractionStyleView.swift
    var tapColor    = UIColor.whiteColor()
    var longColor   = UIColor.whiteColor()
    var swipeColor  = UIColor.whiteColor()
    var noneColor   = UIColor.whiteColor()

続いて、プロパティとしてInteractionStyleを保持します。
この時に、didSetで代入されたときに各ジェスチャーの値を取得し、それぞれ対応するUIColorプロパティに代入します。

InteractionStyleView.swift
    var interactionBackgroundColor: InteractionStyle = InteractionStyle.None(UIColor.whiteColor()) {
        didSet{
            switch interactionBackgroundColor {
            case .Tap(let color):
                tapColor = color
            case .LongTap(let color):
                longColor = color
            case .Swipe(let color):
                swipeColor = color
            case .None(let color):
                noneColor = color
            }
        }
    }

最後にawakeFromNibメソッドにUITapGestureRecognizerUILongPressGestureRecognizerUISwipeGestureRecognizerを作成して、ジェスチャーが呼ばれた際には対応するUIColorbackgroundColorプロパティにセットします。

InteractionStyleView.swift
   override func awakeFromNib() {
        self.userInteractionEnabled = true
        
        let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(InteractionStyleView.tapAction(_:)))
        self.addGestureRecognizer(tapGesture)
        
        let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(InteractionStyleView.longAction(_:)))
        self.addGestureRecognizer(longGesture)
        
        let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(InteractionStyleView.swipeAction(_:)))
        self.addGestureRecognizer(swipeGesture)
        
    }
    
    func tapAction(sender: UITapGestureRecognizer){
       backgroundColor = tapColor
    }
    
    func longAction(sender: UILongPressGestureRecognizer){
        backgroundColor = longColor
    }
    
    func swipeAction(sender: UISwipeGestureRecognizer){
        backgroundColor = swipeColor
    }


コード全体では以下のとおりになります。

InteractionStyleView.swift

class InteractionStyleView: UIView {

    enum InteractionStyle {
        case Tap(UIColor)
        case LongTap(UIColor)
        case Swipe(UIColor)
        case None(UIColor)
    }
    var tapColor    = UIColor.whiteColor()
    var longColor   = UIColor.whiteColor()
    var swipeColor  = UIColor.whiteColor()
    var noneColor   = UIColor.whiteColor()
    
    var interactionBackgroundColor: InteractionStyle = InteractionStyle.None(UIColor.whiteColor()) {
        didSet{
            switch interactionBackgroundColor {
            case .Tap(let color):
                tapColor = color
            case .LongTap(let color):
                longColor = color
            case .Swipe(let color):
                swipeColor = color
            case .None(let color):
                noneColor = color
            }
        }
    }
    
    override func awakeFromNib() {
        self.userInteractionEnabled = true
        
        let tapGesture = UITapGestureRecognizer.init(target: self, action: #selector(InteractionStyleView.tapAction(_:)))
        self.addGestureRecognizer(tapGesture)
        
        let longGesture = UILongPressGestureRecognizer(target: self, action: #selector(InteractionStyleView.longAction(_:)))
        self.addGestureRecognizer(longGesture)
        
        let swipeGesture = UISwipeGestureRecognizer(target: self, action: #selector(InteractionStyleView.swipeAction(_:)))
        self.addGestureRecognizer(swipeGesture)
        
    }
    
    func tapAction(sender: UITapGestureRecognizer){
       backgroundColor = tapColor
    }
    
    func longAction(sender: UILongPressGestureRecognizer){
        backgroundColor = longColor
    }
    
    func swipeAction(sender: UISwipeGestureRecognizer){
        backgroundColor = swipeColor
    }
    
}


InteractionStyleViewの呼び出し

StoryboardでInteractionStyleViewを配置してUViewControllerにIBOutletで接続します。

ViewControllerからは、interactionStyleViewinteractionBackgroundColorプロパティを代入すればよくなります。

ViewController.swift
class ViewController: UIViewController {

    @IBOutlet weak var interactionStyleView: InteractionStyleView!
   
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        interactionStyleView.interactionBackgroundColor = InteractionStyleView.InteractionStyle.Tap(UIColor.blackColor())
        interactionStyleView.interactionBackgroundColor = InteractionStyleView.InteractionStyle.LongTap(UIColor.redColor())
        interactionStyleView.interactionBackgroundColor = InteractionStyleView.InteractionStyle.Swipe(UIColor.yellowColor())
    }
}

これを実行するとタップは黒、長押しは赤、スワイプは黄色にビューが変わるようになります。

RS02wFsD0j.gif

このコードの利点

  • 使う方(ViewController)はプロパティ代入すればよくなるからわかりやすくなる

欠点

  • 定義する方(InteractionStyleView)では各状態のプロパティをそれぞれ保持するのでコード量が多くなる?

終わりに

プロパティ代入で状態と値の両方を設定する方法を見ていきました。
何かの訳に立てれば幸いです。

また、もっとやりようがありそうですが、今のところ思いつかなかったです。
もっといい方法があればコメントください。

15
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?