Problem I'm developing a multi-touch ios app that involves tracking multiple touch points and interacting with relative pads. The application's function is to respond to touch events, update the 'touch point status' and perform pad lighting and do some other things. I've encountered some problems with multi-touch management and status updates and pad lighting, and I hope to get some help. Specificlly, when I put two fingers(two touches) on the screen(regard less the order), the pad corresponding to your finger will light up (this is what i want) and do some other thing using callback function1. When I lift my finger 1 / touch1 (no matter which one), the pad corresponding to touch1 should be darkened, but it is not darkened, and the corresponding callback function (that is, inside touchesEnded(with touch1)) is not called. Only when I move touch2 or lift touch 2, the touchesEnded method corresponding to touch1 will be called, that is, the pad corresponding to touch1 will be darkened and the corresponding callback function will be called. This is a very tricky problem because it seriously affects my app experience. It is worth noting that this problem only occurs with two touches, and does not occur with any other number of touches. That is to say, when my screen has many touches and the corresponding pads are lit at the same time, moving or lifting any one of the touches can work normally. This problem only occurs when two touches exist on the screen at the same time and both touches are touched in a static manner (that is, touchesMoved is not triggered). If touch1 or touch2 moves, the function will work normally. Code framework And the code framework is as follows:not all codes
class MultiPadUIView: UIView {
// ViewModel reference
// Update using combine
// Callback closure,
var functionTouchesBegin (()->Void)
var functionTouchesEnded (()->Void)
// Touch and tracking mapping, Thread Safety
// private let stateQueue = DispatchQueue(label: "com.youapp.touchQueue",
// qos: .userInteractive,
// attributes: .concurrent)
private var touchStatus : [Int]
override init(frame: CGRect) {
super.init(frame: frame)
self.isMultipleTouchEnabled = true
self.backgroundColor = .systemBackground
self.layer.cornerRadius = 12
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.isMultipleTouchEnabled = true
backgroundColor = .clear
}
// Invalidate the static image cache, and set needs layout
private func invalidateCache() {
cachedImage = nil
resetTouchStates()
setNeedsLayout()
}
private var padFrames: [CGRect] = []
private var cachedImage: UIImage?
private func resetTouchStates() {
}
private func calculatePadFrames() {
}
override func draw(_ rect: CGRect) {
super.draw(rect)
if cachedImage == nil {
cachedImage = generateStaticImage()
}
cachedImage?.draw(in: bounds)
drawDynamicFeedback()
}
private func generateStaticImage() -> UIImage?
}
// draw dynamic content according to touchStatus
private func drawDynamicFeedback()
}
// Called after setNeedDisplay and every touch on screen, called frequently
override func layoutSubviews() {
super.layoutSubviews()
resetTouchStates()
setNeedsDisplay()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// some updataTouchStatus logic,
if let newIndex = newIndex {
//callback function
functionTouchesBegin?(newIndex)
updateTouchState(oldIndex: nil, newIndex: newIndex)
}
}
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// updataTouchesLogic
// callback
if some condition {
// touches ended closure
functionTouchesBegin?(newIndex)
}
}
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// some logic
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touchesEnded(touches, with: event)
resetTouchStates()
}
}
After asking the AI tool, it said that the possible reason was thread blocking. I used a thread-safe data structure as the implementation of my touch status, but it still did not work properly. And after my debugging, when I kept touch2 still and lifted touch1, the program did not enter touchesEnded() at all. Only after moving touch2 will the touchesEnded() corresponding to lifting touch1 be triggered.
Problem I'm developing a multi-touch ios app that involves tracking multiple touch points and interacting with relative pads. The application's function is to respond to touch events, update the 'touch point status' and perform pad lighting and do some other things. I've encountered some problems with multi-touch management and status updates and pad lighting, and I hope to get some help. Specificlly, when I put two fingers(two touches) on the screen(regard less the order), the pad corresponding to your finger will light up (this is what i want) and do some other thing using callback function1. When I lift my finger 1 / touch1 (no matter which one), the pad corresponding to touch1 should be darkened, but it is not darkened, and the corresponding callback function (that is, inside touchesEnded(with touch1)) is not called. Only when I move touch2 or lift touch 2, the touchesEnded method corresponding to touch1 will be called, that is, the pad corresponding to touch1 will be darkened and the corresponding callback function will be called. This is a very tricky problem because it seriously affects my app experience. It is worth noting that this problem only occurs with two touches, and does not occur with any other number of touches. That is to say, when my screen has many touches and the corresponding pads are lit at the same time, moving or lifting any one of the touches can work normally. This problem only occurs when two touches exist on the screen at the same time and both touches are touched in a static manner (that is, touchesMoved is not triggered). If touch1 or touch2 moves, the function will work normally. Code framework And the code framework is as follows:not all codes
class MultiPadUIView: UIView {
// ViewModel reference
// Update using combine
// Callback closure,
var functionTouchesBegin (()->Void)
var functionTouchesEnded (()->Void)
// Touch and tracking mapping, Thread Safety
// private let stateQueue = DispatchQueue(label: "com.youapp.touchQueue",
// qos: .userInteractive,
// attributes: .concurrent)
private var touchStatus : [Int]
override init(frame: CGRect) {
super.init(frame: frame)
self.isMultipleTouchEnabled = true
self.backgroundColor = .systemBackground
self.layer.cornerRadius = 12
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.isMultipleTouchEnabled = true
backgroundColor = .clear
}
// Invalidate the static image cache, and set needs layout
private func invalidateCache() {
cachedImage = nil
resetTouchStates()
setNeedsLayout()
}
private var padFrames: [CGRect] = []
private var cachedImage: UIImage?
private func resetTouchStates() {
}
private func calculatePadFrames() {
}
override func draw(_ rect: CGRect) {
super.draw(rect)
if cachedImage == nil {
cachedImage = generateStaticImage()
}
cachedImage?.draw(in: bounds)
drawDynamicFeedback()
}
private func generateStaticImage() -> UIImage?
}
// draw dynamic content according to touchStatus
private func drawDynamicFeedback()
}
// Called after setNeedDisplay and every touch on screen, called frequently
override func layoutSubviews() {
super.layoutSubviews()
resetTouchStates()
setNeedsDisplay()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// some updataTouchStatus logic,
if let newIndex = newIndex {
//callback function
functionTouchesBegin?(newIndex)
updateTouchState(oldIndex: nil, newIndex: newIndex)
}
}
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
// updataTouchesLogic
// callback
if some condition {
// touches ended closure
functionTouchesBegin?(newIndex)
}
}
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
// some logic
// mark the view need to be redrawn, and update dynamic content according to touch status
setNeedsDisplay()
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
touchesEnded(touches, with: event)
resetTouchStates()
}
}
After asking the AI tool, it said that the possible reason was thread blocking. I used a thread-safe data structure as the implementation of my touch status, but it still did not work properly. And after my debugging, when I kept touch2 still and lifted touch1, the program did not enter touchesEnded() at all. Only after moving touch2 will the touchesEnded() corresponding to lifting touch1 be triggered.
Share Improve this question asked Feb 16 at 23:59 SilhouetteSilhouette 1 New contributor Silhouette is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct. 1- I think you'll need to give us a minimal reproducible example. Editing the code you pasted enough to get it to build and run doesn't show any problems with tracking the touches. – DonMag Commented Feb 17 at 13:13
2 Answers
Reset to default -1Please try below options -
I'm not sure you have set this or not -
self.multipleTouchEnabled = true
. Please check this as well.If #1 doesnt work, try with the tap gesture property "cancelsTouchesInView" which allows touches to be delivered to the view even if the gesture recognizer is active.
recognizer.cancelsTouchesInView = false
I would recommend trying to use touchesCancelled
function as it is called when the system considers your tap as a cancelled one (for instance, when you drag a finger before raising it as answered here: touchescancelled is called instead of touchesended)