¿Cómo podemos implementar en 3D de toque para comprobar si el usuario pulsa en UIView o de la fuerza de contacto en UIView?

Hay una manera de hacer esto con UIGestureRecognize o sólo con UITouch?

InformationsquelleAutor Avi Rok | 2015-09-24

5 Comentarios

  1. 21

    Se puede hacer sin designado un gesto de reconocimiento. Usted no necesita ajustar el touchesEnded y touchesBegan método, sino simplemente el touchesMoved para obtener los valores correctos. conseguir la fuerza de un uitouch de empezaron y terminaron volverá extraño valores.

    UITouch *touch = [touches anyObject];
    
    CGFloat maximumPossibleForce = touch.maximumPossibleForce;
    CGFloat force = touch.force;
    CGFloat normalizedForce = force/maximumPossibleForce;

    a continuación, establezca un umbral de fuerza y comparar los normalizedForce a este umbral (0.75 parece bien para mí).

    • ¿Por qué no acaba de proporcionar el código en su totalidad..
  2. 9

    El 3D Touch propiedades están disponibles en UITouch objetos.

    Usted puede obtener estos toques reemplazando un UIView‘s touchesBegan: y touchesMoved: métodos. No estoy seguro de lo que usted ve en touchesEnded: todavía.

    Si usted está dispuesto a crear nuevos gestos reconocedores, usted tiene acceso completo a la UITouches como expuesto en UIGestureRecognizerSubclass.

    No estoy seguro de cómo se puede utilizar el 3D touch propiedades en un tradicional UIGestureRecognizer. Tal vez a través de la UIGestureRecognizerDelegate del protocolo de gestureRecognizer:shouldReceiveTouch: método.

    • Quiero comprobar si el usuario pulsa sobre el UIView o de la fuerza de contacto en UIView
    • UITapGestureRecognizer no ha sido actualizado para 3D Touch, por lo que tendría que hacer su propio UIGestureRecognizer subclase o subclase de los puntos de vista y manejar touchesBegan y touchesMoved.
    • Si un contacto no se mueve, pero es la fuerza de los cambios hace touchesMoved: se llama?
  3. 6

    Con Swift 4.2 y iOS 12, un camino posible para resolver su problema es crear una subclase personalizada de UIGestureRecognizer que se encarga de la Fuerza de Contacto y agregar a la vista junto a un UITapGestureRecognizer. El siguiente código completo se muestra cómo ponerlo en práctica:

    ViewController.swift

    import UIKit
    class ViewController: UIViewController {
    let redView = UIView()
    lazy var tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(tapHandler))
    lazy var forceTouchGestureRecognizer = ForceTouchGestureRecognizer(target: self, action: #selector(forceTouchHandler))
    override func viewDidLoad() {
    super.viewDidLoad()
    redView.backgroundColor = .red    
    redView.addGestureRecognizer(tapGestureRecognizer)
    view.addSubview(redView)
    redView.translatesAutoresizingMaskIntoConstraints = false
    redView.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
    redView.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
    redView.widthAnchor.constraint(equalToConstant: 100).isActive = true
    redView.heightAnchor.constraint(equalToConstant: 100).isActive = true
    }
    override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
    super.traitCollectionDidChange(previousTraitCollection)
    if traitCollection.forceTouchCapability == UIForceTouchCapability.available {
    redView.addGestureRecognizer(forceTouchGestureRecognizer)
    } else  {
    //When force touch is not available, remove force touch gesture recognizer.
                //Also implement a fallback if necessary (e.g. a long press gesture recognizer)
                redView.removeGestureRecognizer(forceTouchGestureRecognizer)
    }
    }
    @objc func tapHandler(_ sender: UITapGestureRecognizer) {
    print("Tap triggered")
    }
    @objc func forceTouchHandler(_ sender: ForceTouchGestureRecognizer) {
    UINotificationFeedbackGenerator().notificationOccurred(.success)
    print("Force touch triggered")
    }
    }

    ForceTouchGestureRecognizer.swift

    import UIKit.UIGestureRecognizerSubclass
    @available(iOS 9.0, *)
    final class ForceTouchGestureRecognizer: UIGestureRecognizer {
    private let threshold: CGFloat = 0.75
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesBegan(touches, with: event)
    if let touch = touches.first {
    handleTouch(touch)
    }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesMoved(touches, with: event)
    if let touch = touches.first {
    handleTouch(touch)
    }
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesEnded(touches, with: event)
    state = UIGestureRecognizer.State.failed
    }
    override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesCancelled(touches, with: event)
    state = UIGestureRecognizer.State.failed
    }
    private func handleTouch(_ touch: UITouch) {
    guard touch.force != 0 && touch.maximumPossibleForce != 0 else { return }
    if touch.force / touch.maximumPossibleForce >= threshold {
    state = UIGestureRecognizer.State.recognized
    }
    }
    }

    Fuentes:

  4. 5

    La forma en que estoy haciendo esto es el uso de una combinación de un UITapGestureRecognizer (proporcionado por Apple) y un DFContinuousForceTouchGestureRecognizer (por mí).

    La DFContinuousForceTouchGestureRecognizer es agradable, ya que proporciona actualizaciones continuas sobre los cambios de la presión, de modo que usted puede hacer cosas como aumentar el ver como el usuario varía su presión sobre ella, frente a un solo evento. Si solo quieres un evento puede ignorar todo en la DFContinuousForceTouchDelegate excepto el - (void) forceTouchRecognized de devolución de llamada.

    https://github.com/foggzilla/DFContinuousForceTouchGestureRecognizer

    Puede descargar este y ejecutar la aplicación de ejemplo en un dispositivo que soporta la fuerza de la prensa para ver cómo se siente.

    En su UIViewController implementar los siguientes:

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

    implementar selector de llave gesto

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

    Implementar el delegado de protocolo para la fuerza de contacto:

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

    Tenga en cuenta que esto sólo será útil en un dispositivo que soporta la fuerza de contacto.

    También no se debe agregar el DFContinuousForceTouchGestureRecognizer a ver si se ejecuta en iOS 8 o bajo, ya que utiliza la nueva force propiedad en UITouch sólo disponible en iOS 9.

    Si se agrega esto en iOS 8 bloqueo, por lo que condicionalmente agregar este reconocedor, basado en lo que la versión de iOS que se ejecuta en si admite las versiones antiguas de iOS 9.

    • Wow gracias! Puede convertir su respuesta a la Swift 2.0? Y mi apoyo app iOS 8 – 9, así que no necesito agregar DFContinuousForceTouchGestureRecognizer para ver
  5. 5

    He creado un UIGestureRecognizer que emula el comportamiento de la aplicación de Mail de Apple. En 3D touch, se inicia con un breve pulso único que vibre y, a continuación, un opcional de acción secundaria (hardTarget) y el pulso llamado por duro presionando poco después de la inicial de prensa.

    Adaptado de https://github.com/FlexMonkey/DeepPressGestureRecognizer

    Cambios:

    • 3D toque vibrar de pulsos como iOS comportamiento del sistema
    • toque tiene que venir por ella hasta el final, como la aplicación de mail de Apple
    • umbral predeterminado nivel predeterminado del sistema
    • duro contacto desencadena hardAction llamada como la aplicación de correo

    Nota: he añadido los indocumentados sistema de sonido k_PeakSoundID, pero se sienten libres para apagarla si no se siente cómodo usando una constante más allá de la gama documentado. He estado usando los sonidos del sistema no divulgada constantes durante años, pero se le dio la bienvenida a desactivar la vibración de pulsos mediante el vibrateOnDeepPress de la propiedad.

    import UIKit
    import UIKit.UIGestureRecognizerSubclass
    import AudioToolbox
    class DeepPressGestureRecognizer: UIGestureRecognizer {
    var vibrateOnDeepPress = true
    var threshold: CGFloat = 0.75
    var hardTriggerMinTime: TimeInterval = 0.5
    var onDeepPress: (() -> Void)?
    private var deepPressed: Bool = false {
    didSet {
    if (deepPressed && deepPressed != oldValue) {
    onDeepPress?()
    }
    }
    }
    private var deepPressedAt: TimeInterval = 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>, with event: UIEvent) {
    if let touch = touches.first {
    handle(touch: touch)
    }
    }
    override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent) {
    if let touch = touches.first {
    handle(touch: touch)
    }
    }
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
    super.touchesEnded(touches, with: event)
    state = deepPressed ? UIGestureRecognizerState.ended : UIGestureRecognizerState.failed
    deepPressed = false
    }
    private func handle(touch: UITouch) {
    guard let _ = view, touch.force != 0 && touch.maximumPossibleForce != 0 else {
    return
    }
    let forcePercentage = (touch.force / touch.maximumPossibleForce)
    let currentTime = Date.timeIntervalSinceReferenceDate
    if !deepPressed && forcePercentage >= threshold {
    state = UIGestureRecognizerState.began
    if vibrateOnDeepPress {
    AudioServicesPlaySystemSound(k_PeakSoundID)
    }
    deepPressedAt = Date.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.perform(hardAction, with: 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(gestureRecognizer: deepPressGestureRecognizer)
    }
    func removeDeepPressAction() {
    guard let gestureRecognizers = gestureRecognizers else { return }
    for recogniser in gestureRecognizers where recogniser is DeepPressGestureRecognizer {
    removeGestureRecognizer(gestureRecognizer: recogniser)
    }
    }
    }

Dejar respuesta

Please enter your comment!
Please enter your name here