LOADING

文字入力時のようなUIButtonを起点としたフリック動作をつける【Swift】

公開日
更新日

iPhoneで文字入力をする場合多くの人はフリックで入力していると思います。

2016-03-文字入力時のようなUIButtonを起点としたフリック動作をつける【Swift】-eb3aa2b88b184f0b9037b232ba54d7e7

しゅっしゅっしゅ〜ってやるやつです

 

こんな感じの動作をする方法を紹介します〜(もっといい方法もあるかもです )

今回は上にフリックするとカウントが1増え、下にフリックするとカウントが1減るというのをしてみました。

UIButtonとUISwipeGestureRecognizerを用いて実装していこうと思います。

 

起点となるボタンを用意する

まずは適当にボタンを用意します。

//画面の高さと幅を取得
let screenWidth:Double = Double(UIScreen.mainScreen().bounds.size.width)
let screenHeight:Double = Double(UIScreen.mainScreen().bounds.size.height)

//ボタンを作成する
let btn = UIButton()
btn.frame = CGRectMake(0,0,50,50)
btn.layer.position = CGPoint(x:screenWidth / 2.0,y:screenHeight / 2.0)
btn.setTitle("0",forState:.Normal)
btn.setTitleColor(UIColor.blackColor(),forState:.Normal)
self.view.addSubview(btw)

このボタンにアクションを加えていくのですが、

ボタンをタップで+−のラベルを表示する → 指が離れたらラベルを非表示にする

というアクションをつけます。

//指が触れた時点でのアクション
btn.addTarget(self,action:"btnTouch:",forControlEvents:.TouchDown)

//指が離れた時点でのアクション
btn.addTarget(self,action:"btnCancel:",forControlEvents:.TouchCancel)
btn.addTarget(self,action:"btnCancel:",forControlEvents:.TouchUpInside)
btn.addTarget(self,action:"btnCancel:",forControlEvents:.TouchDragOutside)

指が離れた時点、というのがイマイチ定義しづらくて、タッチキャンセルをしたとき、タッチしてそのまま指を離した時、タッチ→ドラッグしてボタンの外で離した時の3種類でごまかしてます。

この3つを書いておけばラベルが残るってことはあまりないと思います。。。今の所。。。

 

とりあえずタップ時の動作は置いといて、

UISwipeRecognizerをUIButtonにつける

そしてこのボタンにswipe動作をつけます。

//上にスワイプした時の動作
let swipeUp = UISwipeGestureRecognizer()
swipeUp.direction = UISwipeGestureRecognizerDirection.Up
swipeUp.addTarget(self, action:"countUp:")
btn.addGestureRecognizer(swipeUp)
//下にスワイプした時の動作
let swipeDown = UISwipeGestureRecognizer()
swipeDown.direction = UISwipeGestureRecognizerDirection.Down
swipeDown.addTarget(self, action:"countDown:")
btn.addGestureRecognizer(swipeDown)

directionはスワイプする向きです

これを先ほどのUIButtonにaddしてください。

 

ボタンをタップした時にラベルを表示する

//upLabelとdownLabelを用意
var upLabel:UILabel?
var downLabel:UILabel?

//ボタンに触れた時のアクション
func btnTouch(sender:UIButton){
        print("touch")
        if upLabel == nil{
            upLabel = UILabel()
            upLabel!.frame = CGRectMake(0,0,sender.frame.width,sender.frame.height)
            //起点となるボタンを支点とする
            upLabel!.layer.position = CGPoint(x:sender.layer.position.x,y:sender.layer.position.y - sender.frame.height)
            upLabel!.backgroundColor = UIColor(red:0.7,green:0.7,blue:0.7,alpha:0.7)
            upLabel!.text = "+"
            upLabel!.font = UIFont.systemFontOfSize(22)
            upLabel!.textAlignment = NSTextAlignment.Center
            self.view.addSubview(upLabel!)
        }
        if downLabel == nil{
            downLabel = UILabel()
            downLabel!.frame = CGRectMake(0,0,sender.frame.width,sender.frame.height)
            downLabel!.layer.position = CGPoint(x:sender.layer.position.x,y:sender.layer.position.y + sender.frame.height)
            downLabel!.backgroundColor = UIColor(red:0.7,green:0.7,blue:0.7,alpha:0.7)
            downLabel!.text = "−"
            downLabel!.font = UIFont.systemFontOfSize(22)
            downLabel!.textAlignment = NSTextAlignment.Center
            self.view.addSubview(downLabel!)
        }
}

 

upLabelとdownLabelがnilであるかのチェックはしておきましょう。

ボタンキャンセル時は

func btnCancel(sender:UIButton){
        print("離れた")
        if upLabel != nil && downLabel != nil{
            //upLabelとdownLabelを削除する
            self.upLabel!.removeFromSuperview()
            self.downLabel!.removeFromSuperview()
            self.upLabel = nil
            self.downLabel = nil
        }
}

 

こんな感じでhiddenで隠すのではなくnilにしちゃってます。

 

Swipe時のアクション

あとはSwipeされた時のアクションを書いていきます

//上にスワイプした時
func countUp(sender:UISwipeGestureRecognizer){
        var count = Int(btn!.titleLabel!.text!)
        count!++
        let stringCount = String(count!)
        btn!.setTitle(stringCount,forState:.Normal)
}
//下にスワイプした時
func countDown(sender:UISwipeGestureRecognizer){
        var count = Int(btn!.titleLabel!.text!)
        count!--
        let stringCount = String(count!)
        btn!.setTitle(stringCount,forState:.Normal)
}

countには元々入っているテキストの値を入れてそこから上下させています。

 

これで上にスワイプした時はカウントが増え、下にスワイプをした時はカウントが減る、というものができたはずです。。

 

左右にスワイプした時の動作も同様にしてつければキーボードのようなものも作れるはず・・・です!

左右の場合はUISwipeGestureRecognizerDirectionをLeftまたはRightにすればOKです。

 

全体のコード

import UIKit

class ViewController: UIViewController{
    //画面の高さと幅を取得
    let screenWidth:Double = Double(UIScreen.mainScreen().bounds.size.width)
    let screenHeight:Double = Double(UIScreen.mainScreen().bounds.size.height)
    var btn:UIButton?
    override func viewDidLoad() {
        super.viewDidLoad()
        //ボタンを作成する
        btn = UIButton()
        btn!.frame = CGRectMake(0,0,50,50)
        btn!.layer.position = CGPoint(x:screenWidth / 2.0,y:screenHeight / 2.0)
        btn!.setTitle("0",forState:.Normal)
        btn!.setTitleColor(UIColor.blackColor(),forState:.Normal)
        
        //指が触れた時点でのアクション
        btn!.addTarget(self,action:"btnTouch:",forControlEvents:.TouchDown)
        
        //指が離れた時点でのアクション
        btn!.addTarget(self,action:"btnCancel:",forControlEvents:.TouchCancel)
        btn!.addTarget(self,action:"btnCancel:",forControlEvents:.TouchUpInside)
        btn!.addTarget(self,action:"btnCancel:",forControlEvents:.TouchDragOutside)
        self.view.addSubview(btn!)
        
        //上にスワイプした時の動作
        let swipeUp = UISwipeGestureRecognizer()
        swipeUp.direction = UISwipeGestureRecognizerDirection.Up
        swipeUp.addTarget(self, action:"countUp:")
        btn!.addGestureRecognizer(swipeUp)
        //下にスワイプした時の動作
        let swipeDown = UISwipeGestureRecognizer()
        swipeDown.direction = UISwipeGestureRecognizerDirection.Down
        swipeDown.addTarget(self, action:"countDown:")
        btn!.addGestureRecognizer(swipeDown)
    }
    
    //upLabelとdownLabelを用意
    var upLabel:UILabel?
    var downLabel:UILabel?
    
    //ボタンに触れた時のアクション
    func btnTouch(sender:UIButton){
        print("touch")
        if upLabel == nil{
            upLabel = UILabel()
            upLabel!.frame = CGRectMake(0,0,sender.frame.width,sender.frame.height)
            //起点となるボタンを支点とする
            upLabel!.layer.position = CGPoint(x:sender.layer.position.x,y:sender.layer.position.y - sender.frame.height)
            upLabel!.backgroundColor = UIColor(red:0.7,green:0.7,blue:0.7,alpha:0.7)
            upLabel!.text = "+"
            upLabel!.font = UIFont.systemFontOfSize(22)
            upLabel!.textAlignment = NSTextAlignment.Center
            self.view.addSubview(upLabel!)
        }
        if downLabel == nil{
            downLabel = UILabel()
            downLabel!.frame = CGRectMake(0,0,sender.frame.width,sender.frame.height)
            downLabel!.layer.position = CGPoint(x:sender.layer.position.x,y:sender.layer.position.y + sender.frame.height)
            downLabel!.backgroundColor = UIColor(red:0.7,green:0.7,blue:0.7,alpha:0.7)
            downLabel!.text = "−"
            downLabel!.font = UIFont.systemFontOfSize(22)
            downLabel!.textAlignment = NSTextAlignment.Center
            self.view.addSubview(downLabel!)
        }
    }
    
    //ボタンから指が離れた時
    func btnCancel(sender:UIButton){
        print("離れた")
        if upLabel != nil && downLabel != nil{
            //upLabelとdownLabelを削除する
            self.upLabel!.removeFromSuperview()
            self.downLabel!.removeFromSuperview()
            self.upLabel = nil
            self.downLabel = nil
        }
    }
    //上にスワイプした時
    func countUp(sender:UISwipeGestureRecognizer){
        var count = Int(btn!.titleLabel!.text!)
        count!++
        let stringCount = String(count!)
        btn!.setTitle(stringCount,forState:.Normal)
    }
    //下にスワイプした時
    func countDown(sender:UISwipeGestureRecognizer){
        var count = Int(btn!.titleLabel!.text!)
        count!--
        let stringCount = String(count!)
        btn!.setTitle(stringCount,forState:.Normal)
    }
}

 

optionalの扱いが恐らく上手くないので分かりづらいとは思いますが。

2016-03-文字入力時のようなUIButtonを起点としたフリック動作をつける【Swift】-ab9789dc59bf4574a9d72cabe831c4b6

このコードそのままコピペすると上のような感じになります。ちょっとそろそろgifで載せる方法を学ぼう分かりづらいですね・・・。

+と−はタップした時だけ見えます。

厳密にはフリックとスワイプって異なると思うんですが、うーむとりあえず上手く動いてはいます。

実際のキーボードのフリックは座標を用いてるのかなぁとは思いますが、このくらいならこの方法でも十分かなとも思います。ゆーーっくりドラッグして+上で離す、という動作には対応できてないのは少しむむって感じですが。

色々と応用が利きそうなので使っていきたい。

参考リンク


追記

(追記日: 2018-04-28)

こんなこともしたなぁと思いながらSwift4で、今の自分の書き方で書いてみました。

import UIKit
@objc protocol UpDownCountButtonDelegate {
func touchDown(sender:UIButton)
func touchCancel(sender:UIButton)
}
class UpDownCountButton: UIButton {
var delegate:UpDownCountButtonDelegate!
var count = 0 {
didSet {
self.setTitle(String(count), for: .normal)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
}
convenience init(frame:CGRect,delegate:UpDownCountButtonDelegate) {
self.init(frame: frame)
self.delegate = delegate
setup()
}
func setup() {
self.setTitle("0",for:.normal)
self.setTitleColor(UIColor.black,for:.normal)
self.backgroundColor = UIColor.lightGray
//指が触れた時点でのアクション
self.addTarget(delegate,action:#selector(delegate.touchDown(sender:)),for:.touchDown)
//キャンセル処理
self.addTarget(delegate, action: #selector(delegate.touchCancel(sender:)), for: .touchCancel)
self.addTarget(delegate, action: #selector(delegate.touchCancel(sender:)), for: .touchUpInside)
self.addTarget(delegate, action: #selector(delegate.touchCancel(sender:)), for: .touchDragOutside)
//上にスワイプした時の動作
let swipeUp = UISwipeGestureRecognizer()
swipeUp.direction = .up
swipeUp.addTarget(self, action: #selector(swipe(sender:)))
self.addGestureRecognizer(swipeUp)
//下にスワイプした時の動作
let swipeDown = UISwipeGestureRecognizer()
swipeDown.direction = .down
swipeDown.addTarget(self, action:#selector(swipe(sender:)))
self.addGestureRecognizer(swipeDown)
}
@objc func swipe(sender:UISwipeGestureRecognizer){
//countする
switch sender.direction{
case .up: count += 1
case .down: count -= 1
default:break
}
}
}
import UIKit
class UpDownCountView: UIView {
var plusLabel:UILabel!
var minusLabel:UILabel!
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
func setup() {
let width = self.frame.width
let height = self.frame.height
let itemHeight = height / 3
let btnRect = CGRect(x:0,y:height / 3,width:width,height:itemHeight)
let btn = UpDownCountButton(frame: btnRect, delegate: self)
self.addSubview(btn)
let plusRect = CGRect(x:0,y:0,width:width,height:itemHeight)
plusLabel = UILabel(frame:plusRect)
plusLabel.text = "+"
plusLabel.textAlignment = .center
plusLabel.backgroundColor = UIColor.red
self.addSubview(plusLabel)
let minusRect = CGRect(x:0,y:height * 2 / 3,width:width,height:itemHeight)
minusLabel = UILabel(frame:minusRect)
minusLabel.text = "−"
minusLabel.textAlignment = .center
minusLabel.backgroundColor = UIColor.blue
self.addSubview(minusLabel)
//隠しておく
plusLabel.isHidden = true
minusLabel.isHidden = true
}
}
extension UpDownCountView:UpDownCountButtonDelegate {
func touchDown(sender: UIButton) {
self.plusLabel.isHidden = false
self.minusLabel.isHidden = false
}
func touchCancel(sender: UIButton) {
self.plusLabel.isHidden = true
self.minusLabel.isHidden = true
}
}
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let width = self.view.frame.width
let height = self.view.frame.height
let itemWidth:CGFloat = 50
let itemHeight = itemWidth * 3 //3倍にしておく
let upDownView = UpDownCountView(frame:CGRect(x:0,y:0,width:itemWidth,height:itemHeight))
upDownView.center = CGPoint(x:width / 2,y:height / 2)
self.view.addSubview(upDownView)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

gif <img src="/images/images/upDownCount.gif" alt="images/upDownCount.gif" loading="lazy" />

クリックでのスワイプが難しい。

スワイプだけでなく指が離れた時に+ラベルや−ラベルの上にあるかどうかの判定をした方が使いやすさはアップする気がします。 アクションが重複するかもしれないことを考えるとちょっと面倒ですが(´ε`;)ウーン…

暇があればそういう風に追記するかもしれませんがとりあえずはこれで。

公開日
更新日