2015-09-24 25 views
29

はどのようにして、ユーザがUIViewをタップかどうかを確認またはUIView上のタッチを強制するために、3Dタッチを実現することができますか?3Dタッチ/フォースタッチ実装

UIGestureRecognizeのみUITouchとでこれを行う方法はありますか?

答えて

16

よりも古いバージョンをサポートしている場合は、上で実行されているどのようなiOSのバージョンに基づいて、この認識機能を追加します。 touchesEndedメソッドとtouchesBeganメソッドを調整する必要はありませんが、単にtouchesMovedを使用して正しい値を取得するだけです。 uitouchの力を開始/終了から奇妙な値を返します。その後、

UITouch *touch = [touches anyObject]; 

CGFloat maximumPossibleForce = touch.maximumPossibleForce; 
CGFloat force = touch.force; 
CGFloat normalizedForce = force/maximumPossibleForce; 

、(0.75が私のために罰金だ)力のしきい値を設定し、このしきい値にnormalizedForceを比較します。

+6

を追加する必要はありません。 – Esqarrouth

8

3Dタッチのプロパティare available on UITouch objects

UIViewtouchesBegan:touchesMoved:のメソッドをオーバーライドすることで、これらの接触を得ることができます。 touchesEnded:に表示される内容はまだわかりません。

新しいジェスチャーレコグナイザを作成したい場合は、UIGestureRecognizerSubclassに公開されているUITouchに完全にアクセスできます。

私はあなたが伝統的なUIGestureRecognizerでの3Dタッチのプロパティを使用することができますかどうかはわかりません。おそらくUIGestureRecognizerDelegateプロトコルのgestureRecognizer:shouldReceiveTouch:メソッドを介して。

+0

私はUIViewの –

+0

上のUIViewまたは力touchのユーザータップは 'UITapGestureRecognizer'が3D Touch用更新されていないので、あなたがあなた自身の' UIGestureRecognizer'サブクラスを作るか、またはビューをサブクラス化しなければならないかどうかを確認したいです'touchesBegan'と' touchesMoved'を処理します。 –

+1

タッチが動かずに強制変更された場合、 'touchesMoved:'が呼び出されますか? – Warpling

5

私がこれをやっているのは、UITapGestureRecognizer(Apple提供)とDFContinuousForceTouchGestureRecognizer(私が提供)の組み合わせを使用することです。

DFContinuousForceTouchGestureRecognizerは、気圧変化について継続的な更新を提供するので、ユーザーが圧力に変化するにつれてビューを増やすことができます(単一のイベントではなく)。単一のイベントだけが必要な場合は、の- (void) forceTouchRecognizedコールバックを除いて、eveythingを無視することができます。

https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer

あなたはこれをダウンロードし、それを感じているかを確認するために力押しをサポートするデバイス上でサンプルアプリを実行することができます。

- (void)viewDidLoad { 
    [super viewDidLoad]; 
    _forceTouchRecognizer = [[DFContinuousForceTouchGestureRecognizer alloc] init]; 
    _forceTouchRecognizer.forceTouchDelegate = self; 

    //here to demonstrate how this works alonside a tap gesture recognizer 
    _tapGestureRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(tapped:)]; 

    [self.imageView addGestureRecognizer:_tapGestureRecognizer]; 
    [self.imageView addGestureRecognizer:_forceTouchRecognizer]; 
} 

力touch用のデリゲートプロトコルの実装タップジェスチャーのため

#pragma UITapGestureRecognizer selector 

- (void)tapped:(id)sender { 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     [[[UIAlertView alloc] initWithTitle:@"Tap" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 
    }); 
} 

をセレクタを実装します:これはのみすること

#pragma DFContinuousForceTouchDelegate 

- (void)forceTouchRecognized:(DFContinuousForceTouchGestureRecognizer *)recognizer { 
    self.imageView.transform = CGAffineTransformIdentity; 
    [self.imageView setNeedsDisplay]; 
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(0.1f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ 
     [[[UIAlertView alloc] initWithTitle:@"Force Touch" message:@"YEAH!!" delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil] show]; 
    }); 
} 

- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didStartWithForce:(CGFloat)force maxForce:(CGFloat)maxForce { 
    CGFloat transformDelta = 1.0f + ((force/maxForce)/3.0f); 
    self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta); 
    [self.imageView setNeedsDisplay]; 
} 

- (void) forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didMoveWithForce:(CGFloat)force maxForce:(CGFloat)maxForce { 
    CGFloat transformDelta = 1.0f + ((force/maxForce)/3.0f); 
    self.imageView.transform = CGAffineTransformMakeScale(transformDelta, transformDelta); 
    [self.imageView setNeedsDisplay]; 
} 

- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didCancelWithForce:(CGFloat)force maxForce:(CGFloat)maxForce { 
    self.imageView.transform = CGAffineTransformIdentity; 
    [self.imageView setNeedsDisplay]; 
} 

- (void)forceTouchRecognizer:(DFContinuousForceTouchGestureRecognizer *)recognizer didEndWithForce:(CGFloat)force maxForce:(CGFloat)maxForce { 
    self.imageView.transform = CGAffineTransformIdentity; 
    [self.imageView setNeedsDisplay]; 
} 

- (void)forceTouchDidTimeout:(DFContinuousForceTouchGestureRecognizer *)recognizer { 
    self.imageView.transform = CGAffineTransformIdentity; 
    [self.imageView setNeedsDisplay]; 
} 

注意をごUIViewController次の実装で

フォースタッチをサポートするデバイスで役立つことがあります。あなたはiOSの8それにこれを追加した場合、それはiOSの9

でのみ利用可能UITouchに新しいforceプロパティを使用していますので、あなたはiOSの8上または下で実行されている場合

はまた、あなたは、ビューにDFContinuousForceTouchGestureRecognizerを追加しないでくださいクラッシュし、その条件付きであなたはあなたが指定したジェスチャー認識せずにそれを行うことができますiOSの9

+0

うわー、ありがとう! Swift 2.0へのあなたの答えを変換できますか?私のアプリはiOS 8 - 9をサポートしているので、DFContinuousForceTouchGestureRecognizerを表示するには、 –

3

Apple Mailアプリの動作をエミュレートするUIGestureRecognizerを作成しました。 3次元タッチでは、最初の短いパルスで振動し、その後、最初のプレスの直後にハードプレスで呼び出される任意の2次アクション(ハードターゲット)とパルスが始まります。

  • iOSのシステムの動作のような3Dタッチバイブレーションパルスが
  • タッチがシステムにアップルのメールアプリ
  • しきい値のデフォルトのように、最後までそれを考え出す必要があります。https://github.com/FlexMonkey/DeepPressGestureRecognizer

    変更から適応

    デフォルトレベル

  • ハード・タッチ・トリガーhardActionコール・メール・アプリケーション

注:文書化されていないシステムサウンドk_PeakSoundIDを追加しましたが、文書化された範囲を超えて定数を使用するのが不快なら、それを無効にしてください。私は何年もの間、非公開の定数でシステムサウンドを使用してきましたが、vibrateOnDeepPressプロパティを使用して振動パルスをオフにすることを歓迎しています。

import AudioToolbox 
import UIKit.UIGestureRecognizerSubclass 

class DeepPressGestureRecognizer: UIGestureRecognizer 
{ 
    var vibrateOnDeepPress = true 
    var threshold:CGFloat = 0.75 
    var hardTriggerMinTime:NSTimeInterval = 0.5 

    private var deepPressed: Bool = false 
    private var deepPressedAt: NSTimeInterval = 0 
    private var k_PeakSoundID:UInt32 = 1519 
    private var hardAction:Selector? 
    private var target: AnyObject? 

    required init(target: AnyObject?, action: Selector, hardAction:Selector?=nil, threshold: CGFloat = 0.75) 
    { 
     self.target = target 
     self.hardAction = hardAction 
     self.threshold = threshold 

     super.init(target: target, action: action) 
    } 

    override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent) 
    { 
     if let touch = touches.first 
     { 
      handleTouch(touch) 
     } 
    } 

    override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent) 
    { 
     if let touch = touches.first 
     { 
      handleTouch(touch) 
     } 
    } 

    override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent) 
    { 
     super.touchesEnded(touches, withEvent: event) 

     state = deepPressed ? UIGestureRecognizerState.Ended : UIGestureRecognizerState.Failed 

     deepPressed = false 
    } 

    private func handleTouch(touch: UITouch) 
    { 
     guard let _ = view where touch.force != 0 && touch.maximumPossibleForce != 0 else 
     { 
      return 
     } 

     let forcePercentage = (touch.force/touch.maximumPossibleForce) 
     let currentTime = NSDate.timeIntervalSinceReferenceDate() 

     if !deepPressed && forcePercentage >= threshold 
     { 
      state = UIGestureRecognizerState.Began 

      if vibrateOnDeepPress 
      { 
       AudioServicesPlaySystemSound(k_PeakSoundID) 
      } 

      deepPressedAt = NSDate.timeIntervalSinceReferenceDate() 
      deepPressed = true 
     } 
     else if deepPressed && forcePercentage <= 0 
     { 
      endGesture() 
     } 
     else if deepPressed && currentTime - deepPressedAt > hardTriggerMinTime && forcePercentage == 1.0 
     { 
      endGesture() 

      if vibrateOnDeepPress 
      { 
       AudioServicesPlaySystemSound(k_PeakSoundID) 
      } 

      //fire hard press 
      if let hardAction = self.hardAction, let target = self.target { 
       target.performSelector(hardAction, withObject: self) 
      } 
     } 
    } 

    func endGesture() { 
     state = UIGestureRecognizerState.Ended 
     deepPressed = false 
    } 
} 

// MARK: DeepPressable protocol extension 
protocol DeepPressable 
{ 
    var gestureRecognizers: [UIGestureRecognizer]? {get set} 

    func addGestureRecognizer(gestureRecognizer: UIGestureRecognizer) 
    func removeGestureRecognizer(gestureRecognizer: UIGestureRecognizer) 

    func setDeepPressAction(target: AnyObject, action: Selector) 
    func removeDeepPressAction() 
} 

extension DeepPressable 
{ 
    func setDeepPressAction(target: AnyObject, action: Selector) 
    { 
     let deepPressGestureRecognizer = DeepPressGestureRecognizer(target: target, action: action, threshold: 0.75) 

     self.addGestureRecognizer(deepPressGestureRecognizer) 
    } 

    func removeDeepPressAction() 
    { 
     guard let gestureRecognizers = gestureRecognizers else 
     { 
      return 
     } 

     for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer 
     { 
      removeGestureRecognizer(recogniser) 
     } 
    } 
} 
関連する問題