iOS標準っぽいボタンを自作する


# はじめに

iOS7からUIButtonの見た目がフラットになってしまい、
普通に使うと文字だけの残念なボタンになってしまいました。

今回はそんなiOS標準っぽいボタンを自作してみます。
タッチ処理も含めてのカスタマイズになります。(UIButtonクラスは継承しません)

`swiftバージョンは1.2になります`

完成版は下記のような感じ↓

![screen1.png](https://qiita-image-store.s3.amazonaws.com/0/31823/4b53cb65-618f-a8ee-71a0-7fa444bc6ad3.png "screen1.png")

# 実装

## クラス

UIViewのサブクラスとしてクラスを作成

```swift:
import UIKit

class PMFlatButton: UIView {

}
```

## プロパティ

```swift:
//MARK: Properties
//Public
var lineColor : UIColor = (UIColor(red: 0.294, green: 0.431, blue: 0.988, alpha: 1.000)) {
    didSet{
        contentView.layer.borderColor = lineColor.CGColor
        textLabel.textColor = lineColor
    }
}
var disableColor = UIColor.lightGrayColor()
var disableTextColor = UIColor.whiteColor()
var highlightedTextColor = UIColor.whiteColor()
var textLabel : UILabel!

var highlighted : Bool = false {
    didSet{
        if (highlighted != oldValue){ reloadButtonColors() }
    }
}

var enabled : Bool = true {
    didSet{
        if (enabled != oldValue){
            self.userInteractionEnabled = enabled
            reloadButtonColors()
        }
    }
}

//Private
private var contentView : UIView!
private weak var target : AnyObject?
private var selector : Selector?
private var clickHandler: (() -> Void)?
```

`lineColor`,`highlighted`,`enabled`は値の変更を監視して見た目や挙動を変えられるようにします。

## 初期化

```swift:
//MARK: Initializer
override init(frame: CGRect) {
    super.init(frame: frame)

    self.backgroundColor = UIColor.clearColor()
    self.alpha = 1.0

    contentView = UIView(frame: CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame)))
    contentView.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
    contentView.backgroundColor = UIColor.clearColor()
    contentView.layer.borderColor = lineColor.CGColor
    contentView.layer.borderWidth = 1.0
    contentView.layer.cornerRadius = 5.0
    contentView.layer.masksToBounds = true
    self.addSubview(contentView)

    textLabel = UILabel(frame: CGRectMake(0, 0, CGRectGetWidth(frame), CGRectGetHeight(frame)))
    textLabel.autoresizingMask = UIViewAutoresizing.FlexibleWidth | UIViewAutoresizing.FlexibleHeight
    textLabel.backgroundColor = UIColor.clearColor()
    textLabel.textColor = lineColor
    textLabel.numberOfLines = 0
    textLabel.textAlignment = NSTextAlignment.Center
    textLabel.font = UIFont.systemFontOfSize(14)
    self.addSubview(textLabel)
}
```

## パブリックメソッド

文字のセットとクリック時のハンドラー用関数を追加。
UIButtonのSelector方式と、お手軽に使えるクロージャー方式の2つを用意

```swift:
//MARK: Class Method
func setText(text: NSString){
    textLabel.text = text as String
}

func setTarget(target: AnyObject, selector: Selector){
    self.target = target;
    self.selector = selector
}

func setClickHandler(handler : () -> Void){
    self.clickHandler = handler
}
```

## プライベートメソッド

見た目変更用の関数

```swift:
//MARK: Private Method
private func reloadButtonColors(){
    if(enabled == true){
        contentView.backgroundColor = (highlighted) ? lineColor : UIColor.clearColor()
        textLabel.textColor = (highlighted) ? highlightedTextColor : lineColor
    }else{
        contentView.backgroundColor = disableColor
        contentView.layer.borderColor = disableColor.CGColor
        textLabel.textColor = disableTextColor
    }
}
```

##　タッチイベント

UIViewのタッチイベント関数を使ってボタンのタッチ判定を実装します

```swift:
//MARK: Touch Event
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
    highlighted = true
}

override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
    highlighted = false
}

override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {

    let touch = touches.first as? UITouch

    var touchPoint : CGPoint = CGPointZero
    if let _touch = touch{
        touchPoint = _touch.locationInView(self)
    }

    //タッチ領域から外れた場合はキャンセル扱いにする
    if(touchPoint.x > CGRectGetWidth(self.bounds) || touchPoint.x < 0 || touchPoint.y > CGRectGetHeight(self.bounds) || touchPoint.y < 0){
        touchesCancelled(touches, withEvent: event)
    }
}

override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
    if (!highlighted){
        return
    }

    if (target != nil && selector != nil && target!.respondsToSelector(selector!)){
        var control : UIControl = UIControl()
        control.sendAction(selector!, to: target, forEvent: nil)
    }

    if (clickHandler != nil){
        clickHandler!()
    }

    highlighted = false
}
```

## 使用例

```swift:
var flatButton : PMFlatButton = PMFlatButton(frame: CGRectMake(0, 0, 200, 50))
flatButton.setText("Flat Button")
//クリックハンドラーは下2行のどちらかを使用
flatButton.setTarget(self, selector: "buttonDidPush:")
flatButton.setClickHandler { () -> Void in
    NSLog(__FUNCTION__+" Button clicked")
}
contentView.addSubview(flatButton)

func buttonDidPush(sender : AnyObject){
    NSLog(__FUNCTION__+" Button clicked")
}
```

ソースコードは下記githubにて公開しております。

[PMFlatButton_demo_swift - GitHub](https://github.com/peromasamune/PMFlatButton_demo_swift)

# 終わりに

iOS標準っぽい見た目のボタンができました。
UIViewを継承しているので、いろいろなカスタマイズができます。