Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/ios/99.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 3D触摸/强制触摸实现_Ios_Swift_3dtouch - Fatal编程技术网

Ios 3D触摸/强制触摸实现

Ios 3D触摸/强制触摸实现,ios,swift,3dtouch,Ios,Swift,3dtouch,我们如何实现3D触摸来检查用户是否点击UIView或强制触摸UIView 有没有办法使用uigestureerecognize或仅使用UITouch?三维触摸属性来实现这一点 您可以通过覆盖UIView的touchsbegind:和touchsmoved:方法来获得这些触摸。还不确定您在touchesend:中看到了什么 如果您愿意创建新的手势识别器,您可以完全访问uigesturecognizer子类中公开的UITouches 我不知道如何在传统的UIgestureRecognitor中使用3

我们如何实现3D触摸来检查用户是否点击
UIView
或强制触摸
UIView

有没有办法使用
uigestureerecognize
或仅使用
UITouch

三维触摸属性来实现这一点

您可以通过覆盖
UIView
touchsbegind:
touchsmoved:
方法来获得这些触摸。还不确定您在
touchesend:
中看到了什么

如果您愿意创建新的手势识别器,您可以完全访问
uigesturecognizer子类中公开的
UITouch
es


我不知道如何在传统的
UIgestureRecognitor
中使用3D触摸属性。可能通过
UIGestureRecognitzerDelegate
协议的
GestureRecognitzer:shouldReceiveTouch:
方法。

我这样做的方式是使用a(由苹果提供)和a(由我提供)的组合

dfcontinuousforceTouchGestureRecognitor
很好,因为它提供了压力变化的连续更新,因此您可以在用户改变其压力时(而不是单个事件)增强视图。如果只需要一个事件,则可以忽略
DFContinuousForceTouchDelegate
中的所有内容,但
-(void)forcetouchReceigned
回调除外

您可以下载此应用程序并在支持强制按的设备上运行示例应用程序,以查看其感觉如何

ui视图控制器中
执行以下操作:

- (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];
}
请注意,这仅在支持强制触摸的设备上有用

此外,如果您运行的是iOS 8或更低版本,则不应将
DFContinuousForceTouchGestureRecognitizer
添加到视图中,因为它在
UITouch
上使用了仅在iOS 9中可用的新
force
属性


如果您在iOS 8上添加此识别器,它将崩溃,因此,如果您支持的版本早于iOS 9,请根据您运行的iOS版本有条件地添加此识别器。

您可以在没有指定手势识别器的情况下执行此操作。您不需要调整touchesend和touchesbeent方法,只需调整touchesMoved即可获得正确的值。从开始/结束获取uitouch的力将返回奇怪的值

UITouch *touch = [touches anyObject];

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

然后,设置一个力阈值,并将normalizedForce与该阈值进行比较(0.75对我来说似乎很好)。

我创建了一个UIgestureRecognitor,模拟Apple Mail应用程序的行为。在3D触摸时,它以一个短的单脉冲振动开始,然后是一个可选的辅助动作(硬目标),并在初次按下后不久通过硬按下调用脉冲

改编自

变化:

  • 3D触摸震动脉冲,如iOS系统行为
  • 触摸必须启动才能结束,就像苹果邮件应用程序一样
  • 阈值默认为系统默认级别
  • 硬触摸触发硬操作调用,如邮件应用程序
注意:我添加了未记录的系统声音k_PeakSoundID,但如果您不喜欢使用超出记录范围的常数,请随时关闭该声音。多年来,我一直在使用系统声音和未公开的常数,但欢迎您使用VibrationDepress属性关闭振动脉冲

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)
        }
    }
}
导入UIKit
导入UIKit.UIgestureRecognitizerSubclass
导入音频工具箱
类DeepPressGestureRecognitor:UIGestureRecognitor{
var VibrationDepress=true
风险值阈值:CGFloat=0.75
var HardTrig起始时间:时间间隔=0.5
var ONDEPPRESS:(()->无效)?
私有变量:Bool=false{
迪塞特{
if(深压和深压!=oldValue){
onDeepPress?()
}
}
}
私有变量deepPressedAt:TimeInterval=0
私有变量k_PeakSoundID:UInt32=1519
私人行动:选择器?
私有var目标:AnyObject?
必需的初始化(目标:AnyObject?,操作:选择器,硬操作:选择器?=nil,阈值:CGFloat=0.75){
self.target=目标
self.hardAction=hardAction
self.threshold=阈值
super.init(目标:目标,操作:操作)
}
覆盖func touchesStart(touchs:Set,带有事件:UIEvent){
如果让触摸=先触摸{
手柄(触摸:触摸)
}
}
覆盖功能触摸移动(touchs:Set,带有事件:UIEvent){
如果让触摸=先触摸{
手柄(触摸:触摸)
}
}
覆盖func touchesend(touchs:Set,带有事件:UIEvent){
super.touchesend(触摸,带有:事件)
state=deepPressed?UIGestureRecognizerState.ended:UIGestureRecognizerState.failed
深压=假
}
专用功能手柄(触摸:UITouch){
防护罩允许=查看,触摸.force!=0&&touch.maximumPossibleForce!=0其他{
返回
}
让力百分比=(touch.force/touch.maximumPossibleForce)
设currentTime=Date.timeIntervalSinceReferenceDate
如果!深压&&forcePercentage>=阈值{
state=UIgestureRecognitizerState.state
如果振动,请按{
AudioServicesPlaySystemSound(k_PeakSoundID)
}
deepPressedAt=Date.timeintervalencereferencedate
深压=真
}否则,如果深压和强制百分比硬触发时间和强制百分比==1.0{
结束手势()
如果振动,请按{
AudioServicesPlaySystemSound(k_PeakSoundID)
}
//火硬压机
如果让hardAction=self.hardAction,则让target=self.target{
_=目标。执行(硬操作,带:self)
}
}
}
func end手势()
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")
    }

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

}