2015-09-24 7 views
29

Как мы можем реализовать 3D-касание, чтобы проверить, удаляет ли пользователь UIView или нажимает на UIView?3D touch/Force touch реализация

Есть ли способ сделать это с помощью UIGestureRecognize или только с UITouch?

ответ

16

Вы можете сделать это без специального распознавателя жестов. Вам не нужно настраивать метод touchesEnded и touchhesBegan, но просто прикасаетсяМодуль для получения правильных значений. получение силы uitouch от начала/окончания вернет странные значения.

UITouch *touch = [touches anyObject]; 

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

затем установить порог силы и сравнить normalizedForce этого порога (0,75 кажется нормально для меня).

+6

. Почему бы вам просто не предоставить весь код. – Esqarrouth

8

Свойства 3D Touch are available on UITouch objects.

Вы можете получить эти штрихи, переопределив UIViewtouchesBegan: и touchesMoved: методов. Не уверен, что вы видите в touchesEnded:.

Если вы хотите создать новые распознаватели жестов, у вас есть полный доступ к UITouch es, как показано в UIGestureRecognizerSubclass.

Я не уверен, как вы можете использовать свойства 3D-касания в традиционном UIGestureRecognizer. Возможно, с помощью метода UIGestureRecognizerDelegate протокола gestureRecognizer:shouldReceiveTouch:.

+0

Я хочу, чтобы проверить, если пользователь нажмите на UIView или сила прикосновения к UIView –

+0

'UITapGestureRecognizer' не был обновлен для 3D Touch, так что вы должны сделать свой собственный' UIGestureRecognizer' подкласс или подкласс мнения и обрабатывать 'touchhesBegan' и' touchhesMoved'. –

+1

Если прикосновение не двигается, но это изменения в силу, 'touchhesMoved:' get called? – Warpling

5

Способ, которым я это делаю, состоит в использовании комбинации UITapGestureRecognizer (предоставляется Apple) и DFContinuousForceTouchGestureRecognizer (предоставляется мной).

DFContinuousForceTouchGestureRecognizer приятно, потому что он постоянно обновляет информацию о изменениях давления, поэтому вы можете делать такие вещи, как увеличение вида, поскольку пользователь меняет свое давление на него, в отличие от одного события. Если вам просто нужно одно событие, вы можете игнорировать eveything в DFContinuousForceTouchDelegate, за исключением обратного вызова - (void) forceTouchRecognized.

https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer

Вы можете скачать этот и запустить пример приложения на устройстве, которое поддерживает силы нажмите, чтобы увидеть, как это чувствует.

в вашем UIViewController осуществлять следующее:

- (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]; 
} 

реализовать селектор для крана жеста

#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]; 
} 

Обратите внимание, что это будет только быть полезным на устройстве, которое поддерживает силовое прикосновение.

Кроме того, вы не должны добавить DFContinuousForceTouchGestureRecognizer к виду, если вы работаете на 8 прошивке или под, так как он использует новое force свойство на UITouch доступно только в прошивке 9.

Если добавить это на прошивке 8 так ли будет сбой, поэтому условно добавьте этот распознаватель на основе версии iOS, на которой вы работаете, если вы поддерживаете версии старше iOS 9.

+0

Wow thanks! Можете ли вы преобразовать свой ответ в Swift 2.0? И мое приложение поддерживает iOS 8-9, поэтому мне не нужно добавлять DFContinuousForceTouchGestureRecognizer для просмотра –

3

Я создал UIGestureRecognizer, который эмулирует поведение приложения Apple Mail. При 3D-касании он начинается с короткого одиночного импульса, а затем дополнительного вторичного действия (hardTarget) и импульса, вызванного жестким нажатием вскоре после первоначального нажатия.

адаптирован из https://github.com/FlexMonkey/DeepPressGestureRecognizer

Изменения:

  • 3D потрогать вибрируют импульсы как поведение системы IOS
  • прикосновения должно подойти к нему до конца, как Apple Mail приложение
  • пороговых значений по умолчанию в систему уровень по умолчанию
  • твёрдые прикосновения триггеры 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) 
     } 
    } 
} 
Смежные вопросы