LoginSignup
28
36

More than 5 years have passed since last update.

SwiftでcolorPickerを作る

Last updated at Posted at 2015-11-07

何を作りたいか

色を直感的に選択できるアレです。

git → https://github.com/ha1fha1f/colorPicker

スクリーンショット_2015_11_07_22_56.png

色の選択はHSVを元にして、HとSを主な軸として描きました。一番上の段は、V値の低い黒や灰を選択できないので、無理やりたした感じ・・・

軸を分けると直感的に選べない&1タップで色を選べないと思い、二次元で表現することにしました。
真ん中をs=1, v=1にして、左へはsの減少、右へはvの減少、みたいな実装も見かけますが、それだと同じような色が右にも左にもあってわかりにくいと思ったので。

ブロックにしたのは、同じ色を選びたい時に選べるからです。

UIPopoverPresentationController

popOverとして表示させることのできるビューです。iPhoneでも使えるようになりました。(iOS 8.0以降)

ColorPickerViewController()は表示させたいビューのインスタンスを生成しています。そちらは普通のUIViewControllerで、特別な操作は何も必要ありません。

参考 → http://www.minimalab.com/blog/2015/01/30/iphone-uipopovercontroller/

コード全体

ViewController.swift
import UIKit

class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {

    var setColorButton: UIButton! = nil

    override func viewDidLoad() {
        super.viewDidLoad()

        self.view.backgroundColor = UIColor.lightGrayColor()

        if setColorButton == nil {
            setColorButton = UIButton(frame: CGRect(origin: CGPointZero, size: CGSizeMake(50, 50)))
            setColorButton.layer.position = CGPointMake(self.view.bounds.width - 50, self.view.bounds.height - 50)
            setColorButton.backgroundColor = UIColor.redColor()
            setColorButton.addTarget(self, action: "clicked:", forControlEvents: .TouchUpInside)
            self.view.addSubview(setColorButton)
        }
    }

    func clicked(sender: UIButton!) {
        let controller = ColorPickerViewController()
        self.presentPopver(controller, sourceView: sender)
    }

    func presentPopver(viewController: UIViewController!, sourceView: UIView!) {
        viewController.modalPresentationStyle = UIModalPresentationStyle.Popover
        viewController.preferredContentSize = CGSizeMake(300,400)

        let popoverController = viewController.popoverPresentationController
        popoverController?.delegate = self
        // 出す向き(DownはsourceViewの上)
        popoverController?.permittedArrowDirections = UIPopoverArrowDirection.Down
        // どこから出た感じにするか
        popoverController?.sourceView = sourceView
        popoverController?.sourceRect = sourceView.bounds

        self.presentViewController(viewController, animated: true, completion: nil)
    }

    func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
        return UIModalPresentationStyle.None
    }   
}
ColorPicerViewController.swift

class ColorPickerViewController: UIViewController {
    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.redColor()
        print(self.preferredContentSize)
    }
}

説明

iPhoneでもポップアップ表示

func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
    return UIModalPresentationStyle.None
}

これで、iPhoneでもモーダル表示でなく、popup表示を行うことができます。
参考 → https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIAdaptivePresentationControllerDelegate_protocol/index.html#//apple_ref/occ/intfm/UIAdaptivePresentationControllerDelegate/adaptivePresentationStyleForPresentationController:

present

viewController.modalPresentationStyle = UIModalPresentationStyle.Popover
viewController.preferredContentSize = CGSizeMake(300,400)

let popoverController = viewController.popoverPresentationController
popoverController?.delegate = self

// 出す向き(DownはsourceViewの上)
popoverController?.permittedArrowDirections = UIPopoverArrowDirection.Down
// どこから出た感じにするか
popoverController?.sourceView = sourceView
popoverController?.sourceRect = sourceView.bounds

self.presentViewController(viewController, animated: true, completion: nil)

self.presentViewController()でpopupを表示させるのはAlertとかとも同じです。

.sourceViewは吹き出しの矢印がとこから出るかを示します。

.preferredContentSizeで、popover画面のサイズを指定します。遷移後の画面からも、この名前で受け取ることができます。

delegate

今回は不使用ですが、参考 → https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIPopoverPresentationControllerDelegate_protocol/index.html#//apple_ref/occ/intfm/UIPopoverPresentationControllerDelegate/popoverPresentationControllerDidDismissPopover:

これらのdelegateは、dismissViewController()で終了された場合は呼ばれません。popoverViewの画面外をタップすることで終了された場合のみよばれます。

色を描く

お家芸、CoreGraphics
UIViewをおいてもいいですが、無駄なので。

class ColorsView: UIView {
    // 細かさの設定
    var xCount = 15
    var yCount = 20

    var blockSize: CGSize! = nil
    var size: CGSize! = nil

    func setUp() {
        self.size = self.bounds.size
    }

    func colorFromPos(posH: Int, posS: Int) -> UIColor {
        // 白〜黒のやつ
        if posH == 0 {
            return UIColor(hue: 0, saturation: 0, brightness: 1.0-CGFloat(posS)/CGFloat(xCount-1), alpha: 1.0)
        } else {
            return UIColor(hue: CGFloat(posH-1)/CGFloat(yCount-1), saturation: CGFloat(posS+1)/CGFloat(xCount), brightness: 1.0, alpha: 1.0)
        }
    }

    func colorFromPoint(point: CGPoint) -> UIColor {
        let posX = Int(point.x * CGFloat(xCount) / size.width)
        let posY = Int(point.y * CGFloat(yCount) / size.height)
        return colorFromPos(posY, posS: posX)
    }


    override func drawRect(rect: CGRect) {
        let context = UIGraphicsGetCurrentContext()

        let blockSize = CGSizeMake(size.width/CGFloat(xCount), size.height/CGFloat(yCount))

        for i in 0...yCount {
            for j in 0...xCount {
                let color = colorFromPos(i, posS: j)
                color.setFill()
                let blockRect = CGRect(
                    origin: CGPointMake(blockSize.width*CGFloat(j), blockSize.height*CGFloat(i)),
                    size: blockSize
                )
                CGContextFillRect(context, blockRect)
            }
        }
    }
}

はじめUIViewで作ったのもあり、変な実装になってしまいましたが・・・

colorPicker

ViewController.swift
extension ViewController: ColorPickerViewDelegate {
    func onColorChanged(newColor: UIColor) {
        self.setColorButton.backgroundColor = newColor
    }
}

受け取るためのdelegateをimplementして、受け取る。

ColorPickerViewController.swift
protocol ColorPickerViewDelegate {
    func onColorChanged(newColor: UIColor)
}

class ColorPickerViewController: UIViewController {
    var delegate: ColorPickerViewDelegate! = nil

    var colorsView: ColorsView! = nil

    var currentColor: UIColor = UIColor.whiteColor()

    override func viewDidLoad() {
        self.view.backgroundColor = UIColor.redColor()

        self.view.layer.cornerRadius = 1.0

        if colorsView == nil {
            colorsView = ColorsView(frame: CGRect(origin: CGPoint.zero, size: self.preferredContentSize))
            colorsView.setUp()
            self.view.addSubview(colorsView)
        }
    }

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch = touches.first?.locationInView(self.view)
        updateColor(colorsView.colorFromPoint(touch!))
    }

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch = touches.first?.locationInView(self.view)
        updateColor(colorsView.colorFromPoint(touch!))
    }

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
        let touch = touches.first?.locationInView(self.view)
        updateColor(colorsView.colorFromPoint(touch!))

        closeView()
    }

    func closeView() {
        self.dismissViewControllerAnimated(false, completion: nil)
    }

    func updateColor(color: UIColor) {
        self.currentColor = color
        delegate?.onColorChanged(self.currentColor)
    }
}

一番左の列は真っ白である必要が無い(一番左上が真っ白)のでこのような実装にしました。

28
36
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
28
36