Pada 
artikel terakhir, kami melihat bekerja dengan 3DTouch di iPhone. Sekarang tinggal menyelesaikan beberapa pukulan dan menyelesaikan aplikasi kami.

Seperti yang saya katakan, fungsi utama sudah siap, hanya ada peningkatan:
- Ketika mencapai nilai maksimum, saya ingin respons getaran berfungsi seperti pada artikel umpan balik Haptic di iPhone 6s
- Memperbarui nilai dalam UILabel terjadi sangat cepat, (saya pikir Anda perhatikan ini saat pengujian) sehingga Anda perlu menambahkan beberapa kehalusan.
- Lingkaran semitransparan akan muncul pada titik penekanan. Diameternya harus meningkat dengan meningkatnya tekanan dan penurunan dengan penurunan tekanan
Dua poin pertama yang saya tidak bisa tampilkan menggunakan screenshot atau menggunakan animasi 
gif . Karena itu, buat penambahan yang dijelaskan di bawah ini dan periksa sendiri di perangkat Anda. Dan di sini saya akan menunjukkan poin ketiga. Tapi mari kita add-on melakukan semuanya bersama-sama.
Umpan balik getaran Umpan balik haptic
Kami mengimpor kerangka 
AudioToolbox sebelum deklarasi kelas 
ViewController , dan juga menambahkan properti 
isPlaySound untuk mengecualikan 
respons berulang 
dari respons getaran.
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 ... 
Selanjutnya, buat perubahan pada metode 
touchesMoved (: :) sehingga terlihat seperti ini:
  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) " } } } } } 
Tidak ada yang rumit - ketika Anda menjalankan aplikasi iOS, dan dalam hal ini, ketika menginisialisasi 
ViewController dan membuat properti dari kelas 
isPlaySound, kami memungkinkan kemampuan untuk memutar suara - termasuk respons getaran. Ketika tekanan maksimum 
tercapai, isPlaySound (1) dicentang dan jika itu 
benar , getaran dilakukan dan larangan (2) untuk memainkan getaran segera dipicu. Larangan ini diangkat (3) jika gaya penekan menjadi kurang dari nilai maksimum yang dimungkinkan.
Pembaruan lancar
Sekarang tentang kehalusan. Pembaruan label datang dengan sangat cepat, dengan kecepatan respons metode 
touchesMoved (: :) , dan ini beberapa ratus operasi per detik. Untuk mengurangi kecepatan refresh label, saya menambahkan properti dari kelas 
ViewController isUpdate dan mengatur 
pengamat properti 
didSet .
  var isUpdate = true { didSet { if isUpdate == false { DispatchQueue.main.asyncAfter(deadline: .now() + 0.01) { self.isUpdate = oldValue } } } } 
Inti dari konstruksi semacam itu adalah bahwa segera setelah kami menyetel properti ini menjadi false, ia kembali ke true setelah 
0,01 detik. Karenanya, saat menulis teks ke 
UILabel, kami akan menetapkan nilai properti 
isUpdate ke 
false dan tidak akan mengizinkan memperbarui label hingga menjadi 
benar . Karenanya, catatan akan diperbarui dengan kami tidak lebih dari sekali setiap seratus detik.
Dalam metode 
touchesMoved (: :) , di cabang tempat kami menampilkan% kekuatan dan berat dalam gram, ubah kode sebagai berikut:
 if isUpdate { forceLabel.text = "\(Int(force))% force" grammLabel.text = "\(roundGrams) " isUpdate = false } 
Ini akan cukup untuk memberikan kelancaran saat memperbarui label
Sentuh visualisasi
Pertama, mari kita buat 
UIView dan membuatnya tembus dan bulat. Untuk melakukan ini, tambahkan properti dari kelas 
ViewController dan buat pengaturan awal dalam metode 
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 } 
Ada properti, ia memiliki 
View , tetapi pada titik apa ia harus ditambahkan ke layar? Adalah logis bahwa pada saat Anda mulai menyentuh. Di 
ViewController Anda perlu menambahkan metode 
touchesBegan (: :)  override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { if let touch = touches.first { cicrcleView.center = touch.location(in: view)  
Kami juga memilih yang pertama dari set sentuhan dan bekerja dengan sentuhan ini
- Setel pusat circleView ke titik sentuh
- Tambahkan cicrcleView ke layar
Dalam metode 
touchesMoved (: :) di cabang tempat persentase dan berat gram diproses, tambahkan baris:
 cicrcleView.transform = CGAffineTransform.init(scaleX: CGFloat(1 + (grams / 5) / 20), y: CGFloat(1 + (grams / 5) / 20)) 
Di sini kita mengatur matriks transformasi untuk meningkatkan ukuran 
cicrcleView dalam tinggi dan lebar. Nilai-nilai yang saya transfer ke matriks ini adalah hasil dari pemilihan nilai yang paling nyaman. Metode pemilihan "menyodok." Jadi Anda bisa bereksperimen dan memilih nilai-nilai yang sesuai untuk Anda.
Dan akhirnya, di akhir sentuhan, Anda perlu membatalkan transformasi untuk 
cicrleView dan menghapusnya dari layar. Kami sudah memiliki metode untuk mengatasinya. Dalam metode 
touchesEnded (: :) , tambahkan dua baris:
  override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) { forceLabel.text = "0% force" grammLabel.text = "0 "  
Kode 
ViewController lengkap terlihat seperti ini:
 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 } } 
Kode File 
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() } } 
Tautkan proyek di 
GitHub