En el
último artículo, vimos trabajar con 3DTouch en el iPhone. Ahora queda completar un par de trazos y finalizar nuestra aplicación.

Como dije, la funcionalidad principal está lista, solo hay mejoras:
- Al alcanzar el valor máximo, quiero que la respuesta de vibración funcione como en el artículo de retroalimentación háptica en iPhone 6s
- La actualización de los valores en UILabel ocurre muy rápidamente (creo que notó esto cuando realizó la prueba), por lo que debe agregar algo de suavidad.
- Debe aparecer un círculo semitransparente en el punto de presión. Su diámetro debe aumentar al aumentar la presión y disminuir al disminuir la presión.
Los primeros dos puntos que no puedo mostrar usando capturas de pantalla o usando
gifs animados. Por lo tanto, realice las adiciones que se describen a continuación y compruébelo usted mismo en sus dispositivos. Y aquí demostraré el tercer punto. Pero los complementos lo hacemos todos juntos.
Retroalimentación de vibración Retroalimentación háptica
Importamos el marco
AudioToolbox antes de la declaración de la clase
ViewController , y también agregamos la propiedad
isPlaySound para excluir la
respuesta repetida de la
respuesta de vibración.
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 ...
A continuación, realice cambios en el
método touchesMoved (: :) para que se vea así:
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) " } } } } }
Nada complicado: cuando ejecuta la aplicación iOS y, en este caso, cuando inicializa el
ViewController y crea una propiedad de la clase
isPlaySound, habilitamos la capacidad de reproducir sonidos, incluidas las respuestas de vibración. Cuando se alcanza la presión máxima
, se comprueba isPlaySound (1) y, si es
cierto , se realiza la vibración y se activa inmediatamente la prohibición (2) de reproducir la vibración. Esta prohibición se levanta (3) si la fuerza de presión es menor que el valor máximo posible.
Actualización suave
Ahora sobre suavidad. Las actualizaciones de etiquetas llegan muy rápidamente, con la velocidad de respuesta del método
touchesMoved (: :) , y esto es varios cientos de operaciones por segundo. Para reducir la frecuencia de actualización de la etiqueta, agregué una propiedad de la clase
ViewController isUpdate y configuré los
observadores de propiedad
didSet .
var isUpdate = true { didSet { if isUpdate == false { DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { self.isUpdate = oldValue } } } }
La esencia de tal construcción es que tan pronto como establecemos esta propiedad en falso, vuelve a ser verdadera después de
0.01 segundos. En consecuencia, al escribir texto en
UILabel, estableceremos el valor de la propiedad
isUpdate en
falso y no permitiremos actualizar las etiquetas hasta que se convierta en
verdadero . Por lo tanto, los registros se actualizarán con nosotros no más de una vez cada centésima de segundo.
En el
método touchesMoved (: :) , en la rama donde mostramos el% de fuerza y peso en gramos, cambie el código de la siguiente manera:
if isUpdate { forceLabel.text = "\(Int(force))% force" grammLabel.text = "\(roundGrams) " isUpdate = false }
Esto será suficiente para dar suavidad al actualizar etiquetas
Visualización táctil
Primero,
creemos una
UIView y
hagámosla translúcida y redonda. Para hacer esto, agregue una propiedad de la clase
ViewController y realice la configuración inicial en el método
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 }
Hay una propiedad, tiene una
Vista , pero ¿en qué punto debe agregarse a la pantalla? Es lógico que en el momento en que comiences a tocar. En el
ViewController necesita agregar el
método touchesBegan (: :) override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view)
También seleccionamos el primero del conjunto de toques y trabajamos con este toque
- Establecer el centro del círculo Ver al punto de contacto
- Agregue cicrcleView a la pantalla
En el
método touchesMoved (: :) en la rama donde se procesan los porcentajes y el peso en gramos, agregue la línea:
cicrcleView.transform = CGAffineTransform.init(scaleX: CGFloat(1 + (grams / 5) / 20), y: CGFloat(1 + (grams / 5) / 20))
Aquí configuramos la matriz de transformación para aumentar el tamaño de
cicrcleView en altura y ancho. Los valores que transferí a esta matriz son el resultado de seleccionar los valores más convenientes. Método de selección "empuje". Para que pueda experimentar y elegir los valores que le convengan.
Y finalmente, al final del toque, debe cancelar la transformación para
cicrleView y eliminarla de la pantalla. Ya tenemos un método donde puedes resolverlo. En el
método touchesEnded (: :) , agregue dos líneas:
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { forceLabel.text = "0% force" grammLabel.text = "0 "
El código completo de
ViewController se ve así:
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 } }
Código de archivo
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() } }
Proyecto de enlace en
GitHub