Dans le 
dernier article, nous avons envisagé de travailler avec 3DTouch sur l'iPhone. Il ne reste plus qu'à terminer quelques coups et terminer notre application.

Comme je l'ai dit, la fonctionnalité principale est prête, il n'y a que des améliorations:
- Lorsque j'atteins la valeur maximale, je veux que la réponse aux vibrations fonctionne comme dans l'article de rétroaction Haptic sur iPhone 6s
- La mise à jour des valeurs dans UILabel se produit très rapidement (je pense que vous l'avez remarqué lors des tests), vous devez donc ajouter un peu de fluidité.
- Un cercle semi-transparent devrait apparaître au moment d'appuyer. Son diamètre devrait augmenter avec l'augmentation de la pression et diminuer avec la diminution de la pression
Les deux premiers points que je ne peux afficher ni à l'aide de captures d'écran ni à l'aide de 
gifs animés . Par conséquent, effectuez les ajouts décrits ci-dessous et vérifiez-le vous-même sur vos appareils. Et ici, je vais démontrer le troisième point. Mais les modules complémentaires font tout cela ensemble.
Retour de vibration Retour haptique
Nous importons le framework 
AudioToolbox avant la déclaration de la classe 
ViewController et ajoutons également la propriété 
isPlaySound pour exclure la 
réponse répétée de la 
réponse de vibration.
import UIKit import AudioToolbox class ViewController: UIViewController { @IBOutlet weak var scaleView: ScaleView! @IBOutlet weak var forceLabel: UILabel! @IBOutlet weak var grammLabel: UILabel! var isPlaySound = true ... 
Ensuite, apportez des modifications à la 
méthode touchesMoved (: :) pour qu'elle ressemble à ceci:
  override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view) if #available(iOS 9.0, *) { if traitCollection.forceTouchCapability == UIForceTouchCapability.available { if touch.force >= touch.maximumPossibleForce { forceLabel.text = "100%+ force" grammLabel.text = "385 " if isPlaySound { //  // 1 AudioServicesPlaySystemSound(1519) isPlaySound = false // 2 } } else { let force = (touch.force / touch.maximumPossibleForce) * 100 let grams = force * 385 / 100 let roundGrams = Int(grams) isPlaySound = true //  // 3 forceLabel.text = "\(Int(force))% force" grammLabel.text = "\(roundGrams) " } } } } } 
Rien de compliqué - lorsque vous exécutez l'application iOS, et dans ce cas, lors de l'initialisation du 
ViewController et de la création d'une propriété de la classe 
isPlaySound, nous permettons de lire des sons, y compris les réponses vibratoires. Lorsque la pression maximale est 
atteinte, isPlaySound (1) est vérifié et s'il est 
vrai , la vibration est effectuée et l'interdiction (2) de jouer la vibration est immédiatement déclenchée. Cette interdiction est levée (3) si la force de pression devient inférieure à la valeur maximale possible.
Mise à jour fluide
Maintenant sur la douceur. Les mises à jour des étiquettes arrivent très rapidement, avec la vitesse de réponse de la 
méthode touchesMoved (: :) , ce qui représente plusieurs centaines d'opérations par seconde. Pour réduire le taux de rafraîchissement de l'étiquette, j'ai ajouté une propriété de la classe 
ViewController isUpdate et défini les 
observateurs de propriété 
didSet .
  var isUpdate = true { didSet { if isUpdate == false { DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { self.isUpdate = oldValue } } } } 
L'essence d'une telle construction est que dès que nous définissons cette propriété sur false, elle revient à true après 
0,01 seconde. Par conséquent, lors de l'écriture de texte dans 
UILabel, nous 
définirons la valeur de la propriété 
isUpdate sur 
false et n'autoriserons pas la mise à jour des étiquettes jusqu'à ce qu'elle devienne 
vraie . Par conséquent, les enregistrements ne seront mis à jour avec nous qu'une fois tous les centièmes de seconde.
Dans la 
méthode touchesMoved (: :) , dans la branche où nous affichons le% force et poids en grammes, changez le code comme suit:
 if isUpdate { forceLabel.text = "\(Int(force))% force" grammLabel.text = "\(roundGrams) " isUpdate = false } 
Ce sera suffisant pour donner une fluidité lors de la mise à jour des étiquettes
Visualisation tactile
Tout d'abord, créons un 
UIView et 
rendons -le translucide et rond. Pour ce faire, ajoutez une propriété de la classe 
ViewController et 
définissez les paramètres initiaux dans la méthode 
viewDidLoad ()  let cicrcleView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80)) // View 80  80 override func viewDidLoad() { super.viewDidLoad() forceLabel.text = "0% force" grammLabel.text = "0 " cicrcleView.layer.cornerRadius = 40 //      View -   cicrcleView.alpha = 0.6 //  60% cicrcleView.backgroundColor = UIColor.red } 
Il y a une propriété, elle a une 
vue , mais à quel moment faut-il l'ajouter à l'écran? Il est logique qu'au moment où vous commencez à toucher. Dans le 
ViewController, vous devez ajouter la 
méthode touchesBegan (: :)  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view)  
Nous sélectionnons également le premier parmi l'ensemble des touches et travaillons avec cette touche
- Réglez le centre du cercle View sur le point de contact
- Ajouter cicrcleView à l'écran
Dans la 
méthode touchesMoved (: :) dans la branche où les pourcentages et le poids en grammes sont traités, ajoutez la ligne:
 cicrcleView.transform = CGAffineTransform.init(scaleX: CGFloat(1 + (grams / 5) / 20), y: CGFloat(1 + (grams / 5) / 20)) 
Ici, nous définissons la matrice de transformation pour augmenter la taille du 
cicrcleView en hauteur et en largeur. Les valeurs que j'ai transférées dans cette matrice sont le résultat de la sélection des valeurs les plus pratiques. Méthode de sélection "poke". Vous pouvez donc expérimenter et choisir les valeurs qui vous conviennent.
Et enfin, à la fin du toucher, vous devez annuler la transformation de 
cicrleView et la supprimer de l'écran. Nous avons déjà une méthode où vous pouvez travailler. Dans la 
méthode touchesEnded (: :) , ajoutez deux lignes:
  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { forceLabel.text = "0% force" grammLabel.text = "0 "  
Le code complet de 
ViewController ressemble à ceci:
 import UIKit import AudioToolbox class ViewController: UIViewController { @IBOutlet weak var scaleView: ScaleView! @IBOutlet weak var forceLabel: UILabel! @IBOutlet weak var grammLabel: UILabel! var isPlaySound = true var isUpdate = true { didSet { if isUpdate == false { DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { self.isUpdate = oldValue } } } } let cicrcleView = UIView(frame: CGRect(x: 0, y: 0, width: 80, height: 80)) override func viewDidLoad() { super.viewDidLoad() forceLabel.text = "0% force" grammLabel.text = "0 " cicrcleView.layer.cornerRadius = 40 cicrcleView.alpha = 0.6 cicrcleView.backgroundColor = UIColor.red } override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view) // 1 view.addSubview(cicrcleView) // 2 } } override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view) if #available(iOS 9.0, *) { if traitCollection.forceTouchCapability == UIForceTouchCapability.available { if touch.force >= touch.maximumPossibleForce { forceLabel.text = "100%+ force" grammLabel.text = "385 " if isPlaySound { AudioServicesPlaySystemSound(1519) isPlaySound = false } } else { let force = (touch.force / touch.maximumPossibleForce) * 100 let grams = force * 385 / 100 let roundGrams = Int(grams) isPlaySound = true if isUpdate { forceLabel.text = "\(Int(force))% force" grammLabel.text = "\(roundGrams) " isUpdate = false } cicrcleView.transform = CGAffineTransform.init(scaleX: CGFloat(1 + (grams / 5) / 20), y: CGFloat(1 + (grams / 5) / 20)) } } } } } override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { forceLabel.text = "0% force" grammLabel.text = "0 " cicrcleView.removeFromSuperview() cicrcleView.transform = .identity } } 
Code de fichier 
ScaleView :
 import UIKit @IBDesignable class ScaleView: UIView { override func draw(_ rect: CGRect) { let context = UIGraphicsGetCurrentContext() context?.setStrokeColor(UIColor.red.cgColor) context?.setLineWidth(14.0) context?.addArc(center: CGPoint(x: 375 / 2, y: 375 / 2), radius: 375 / 2 - 14, startAngle: 0, endAngle: 2 * CGFloat(M_PI), clockwise: true) context?.strokePath() context?.setLineWidth(1.0) context?.setStrokeColor(UIColor.lightGray.cgColor) context?.addArc(center: CGPoint(x: 375 / 2, y: 375 / 2), radius: 375 / 4 - 14, startAngle: 0, endAngle: 2 * CGFloat(M_PI), clockwise: true) context?.strokePath() } } 
Projet Link sur 
GitHub