ios - generation - swift 3d touch quick actions




3D-Touch/Force-Touch-Implementierung (4)

Dabei verwende ich eine Kombination aus einem UITapGestureRecognizer (von Apple bereitgestellt) und einem DFContinuousForceTouchGestureRecognizer (von mir bereitgestellt).

Der DFContinuousForceTouchGestureRecognizer ist DFContinuousForceTouchGestureRecognizer da er fortlaufende Aktualisierungen der Druckänderungen bereitstellt, sodass Sie beispielsweise die Ansicht erweitern können, wenn der Benutzer den Druck auf sie ändert, und nicht nur ein einzelnes Ereignis. Wenn Sie nur ein einzelnes Ereignis wünschen, können Sie alle Elemente in DFContinuousForceTouchDelegate Ausnahme des Rückrufs - (void) forceTouchRecognized .

DFContinuousForceTouchGestureRecognizer

Sie können diese herunterladen und die Beispiel-App auf einem Gerät ausführen, das Force Press unterstützt, um zu sehen, wie es sich anfühlt.

Implementieren Sie in Ihrem UIViewController Folgendes:

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

Geräteauswahl für Tippgeste

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

Implementieren Sie das Delegate-Protokoll für Force Touch:

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

Beachten Sie, dass dies nur auf Geräten nützlich ist, die Force Touch unterstützen.

Außerdem sollten Sie den DFContinuousForceTouchGestureRecognizer zu einer Ansicht hinzufügen, wenn Sie mit iOS 8 oder darunter arbeiten, da er die neue force Eigenschaft unter UITouch nur in iOS 9 verfügbar ist.

Wenn Sie dies unter iOS 8 hinzufügen, stürzt es ab. Fügen Sie diesen Erkenner also unter bestimmten Bedingungen basierend auf der iOS-Version hinzu, auf der Sie ausgeführt werden, wenn Sie Versionen unterstützen, die älter als iOS 9 sind.

Wie kann 3D-Touch implementiert werden, um zu überprüfen, ob der Benutzer auf UIView tippt oder eine Berührung auf UIView ?

Gibt es eine Möglichkeit, dies mit UIGestureRecognize oder nur mit UITouch zu tun?


Die 3D Touch-Eigenschaften sind für UITouch Objekte verfügbar .

Sie können diese Berührungen UIView , touchesBegan: touchesMoved: Methoden touchesBegan: und touchesMoved: eines UIView touchesMoved: . Nicht sicher, was Sie in touchesEnded: yet sehen.

Wenn Sie neue Gestenerkenner erstellen UITouch , haben Sie vollen Zugriff auf die UITouch es, die in UIGestureRecognizerSubclass .

Ich bin mir nicht sicher, wie Sie die 3D-Touch-Eigenschaften in einem herkömmlichen UIGestureRecognizer . Möglicherweise über die UIGestureRecognizerDelegate gestureRecognizer:shouldReceiveTouch: -Methode des UIGestureRecognizerDelegate Protokolls.


Mit Swift 4.2 und iOS 12 können Sie Ihr Problem möglicherweise lösen, UIGestureRecognizer eine benutzerdefinierte Unterklasse von UIGestureRecognizer , die Force Touch verarbeitet, und diese neben einem UITapGestureRecognizer zu Ihrer Ansicht UITapGestureRecognizer . Der folgende vollständige Code zeigt, wie er implementiert wird:

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
        }
    }

}

Quellen:


Sie können dies ohne eine festgelegte Gestenerkennung tun. Sie müssen nicht die Methode touchesEnded und touchesBegan anpassen, sondern nur die Methode touchesMoved, um die richtigen Werte zu erhalten. Wenn Sie die Kraft eines Uitouchs von Beginn / Ende abrufen, erhalten Sie seltsame Werte.

UITouch *touch = [touches anyObject];

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

Stellen Sie dann einen Kraftschwellenwert ein und vergleichen Sie die normalisierte Force mit diesem Schwellenwert (0,75 scheint mir in Ordnung zu sein).





3dtouch