何を作りたいか
色を直感的に選択できるアレです。
git → https://github.com/ha1fha1f/colorPicker
色の選択は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/
コード全体
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
}
}
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
これらの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
extension ViewController: ColorPickerViewDelegate {
func onColorChanged(newColor: UIColor) {
self.setColorButton.backgroundColor = newColor
}
}
受け取るためのdelegateをimplementして、受け取る。
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)
}
}
一番左の列は真っ白である必要が無い(一番左上が真っ白)のでこのような実装にしました。