Bugs in an app that enables drag and drop with drawing

I have a View called “TestDraw” where I can drag and drop UILables from the collectionView. On the “TestDraw”, free-drawing is also allowed. I have also set BarItems for clearing the canvas. However, for example, if I drag and drop two labels and draw a line then hit clear, and when I try to drag and drop again, the app crashes.

I am using Xcode 10.2, Swift 4.2. The console output when it crashes is:

2019-04-07 12:52:24.346501+0800 TrailingTest[497:23125599] -[OS_dispatch_group convertPoint:fromLayer:]: unrecognized selector sent to instance 0x6000011b13b0
2019-04-07 12:52:24.389911+0800 TrailingTest[497:23125599] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[OS_dispatch_group convertPoint:fromLayer:]: unrecognized selector sent to instance 0x6000011b13b0'
*** First throw call stack:
(
	0   CoreFoundation                      0x000000010e4786fb __exceptionPreprocess + 331
	1   libobjc.A.dylib                     0x000000010da1cac5 objc_exception_throw + 48
	2   CoreFoundation                      0x000000010e496ab4 -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
	3   CoreFoundation                      0x000000010e47d443 ___forwarding___ + 1443
	4   CoreFoundation                      0x000000010e47f238 _CF_forwarding_prep_0 + 120
	5   UIKitCore                           0x0000000117b984e7 -[UIView(Geometry) convertPoint:fromView:] + 102
	6   UIKitCore                           0x0000000117b98126 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 102
	7   CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	8   UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	9   UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	10  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	11  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	12  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	13  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	14  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	15  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	16  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	17  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	18  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	19  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	20  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	21  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	22  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	23  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	24  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	25  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	26  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	27  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	28  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	29  UIKitCore                           0x0000000117b74223 -[UITransitionView hitTest:withEvent:] + 44
	30  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	31  UIKitCore                           0x0000000117b98139 __38-[UIView(Geometry) hitTest:withEvent:]_block_invoke + 121
	32  CoreFoundation                      0x000000010e3a4013 -[__NSArrayM enumerateObjectsWithOptions:usingBlock:] + 483
	33  UIKitCore                           0x0000000117b97ce8 -[UIView(Geometry) hitTest:withEvent:] + 460
	34  UIKitCore                           0x0000000117b981ea -[UIView(Geometry) _hitTest:withEvent:windowServerHitTestWindow:] + 87
	35  UIKitCore                           0x00000001177346bc -[UIWindow _hitTestLocation:inScene:withWindowServerHitTestWindow:event:] + 194
	36  UIKitCore                           0x0000000117734455 __70+[UIWindow _hitTestToPoint:forEvent:windowServerHitTestWindow:screen:]_block_invoke + 159
	37  UIKitCore                           0x0000000117734157 +[UIWindow _topVisibleWindowPassingTest:] + 506
	38  UIKitCore                           0x0000000117734358 +[UIWindow _hitTestToPoint:forEvent:windowServerHitTestWindow:screen:] + 245
	39  UIKitCore                           0x0000000117734571 +[UIWindow _globalHitTestForLocation:inWindowServerHitTestWindow:withEvent:] + 223
	40  UIKitCore                           0x00000001177c9424 -[_UIDragEventSample hitTestWithEvent:constrainToWindowServerHitTestContext:] + 250
	41  UIKitCore                           0x00000001177ca325 -[UIDragEvent _updateGesturesFromCurrentSample] + 202
	42  UIKitCore                           0x00000001177ca171 -[UIDragEvent _updateFromCurrentSample] + 649
	43  UIKitCore                           0x00000001177ca217 -[UIDragEvent _sendIfNeeded] + 72
	44  UIKitCore                           0x00000001171dd4ca __48-[_UIInternalDraggingSessionDestination connect]_block_invoke + 1010
	45  UIKitCore                           0x00000001171e2da9 __59-[_UIDruidDestinationConnection initWithSessionIdentifier:]_block_invoke.1547 + 679
	46  libdispatch.dylib                   0x0000000110ac2db5 _dispatch_client_callout + 8
	47  libdispatch.dylib                   0x0000000110ac62ba _dispatch_block_invoke_direct + 300
	48  FrontBoardServices                  0x0000000119750146 __FBSSERIALQUEUE_IS_CALLING_OUT_TO_A_BLOCK__ + 30
	49  FrontBoardServices                  0x000000011974fdfe -[FBSSerialQueue _performNext] + 451
	50  FrontBoardServices                  0x0000000119750393 -[FBSSerialQueue _performNextFromRunLoopSource] + 42
	51  CoreFoundation                      0x000000010e3dfbe1 __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__ + 17
	52  CoreFoundation                      0x000000010e3df463 __CFRunLoopDoSources0 + 243
	53  CoreFoundation                      0x000000010e3d9b1f __CFRunLoopRun + 1231
	54  CoreFoundation                      0x000000010e3d9302 CFRunLoopRunSpecific + 626
	55  GraphicsServices                    0x0000000113bc62fe GSEventRunModal + 65
	56  UIKitCore                           0x00000001176e7ba2 UIApplicationMain + 140
	57  TrailingTest                        0x000000010d0f1c0b main + 75
	58  libdyld.dylib                       0x0000000110b37541 start + 1
	59  ???                                 0x0000000000000001 0x0 + 1
)
libc++abi.dylib: terminating with uncaught exception of type NSException

This is the function of clearing the canvas and where it is called:

 func ClearCanvas () {
    path.removeAllPoints()
    self.layer.sublayers = nil
    self.setNeedsDisplay()
}

@IBAction func clear(_ sender: UIBarButtonItem) {
    if TestDraw.path != nil{    
        TestDraw.ClearCanvas()
    }
}

I will be very thankful if anyone can help me fix this or at least provide some clues and debugging skills that might help me. Many thanks!

Here is the code for implementing collectionView:

class DocumentViewController: UIViewController,UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, UICollectionViewDragDelegate, UICollectionViewDropDelegate {
var numbersChoices = ["1","2","3","4","5","6","7","8","9","10","11","12","13","14","15","16","17","18","19","20"]
var numberOfCorrectMatches = 0
private var font: UIFont {
    return UIFontMetrics(forTextStyle: .body).scaledFont(for: UIFont.preferredFont(forTextStyle: .body).withSize(60.0))
}
private var numberCardsSet: NumberCardsSet?{
    get{
        let cards = TestDraw.subviews.compactMap{$0 as? UILabel}.compactMap{NumberCardsSet.CardInfo(label: $0)} 
        return NumberCardsSet(numberCards: cards)
    }
    set{
        TestDraw.subviews.compactMap{$0 as? UILabel}.forEach{$0.removeFromSuperview()}
    }
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
    return numbersChoices.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "numberCell", for: indexPath)
    if let numberCell = cell as? numberCollectionViewCell{
        let text = NSAttributedString(string: numbersChoices[indexPath.item], attributes: [.font:font])
        numberCell.label.attributedText = text
    }
    return cell
}

func collectionView(_ collectionView: UICollectionView, itemsForAddingTo session: UIDragSession, at indexPath: IndexPath, point: CGPoint) -> [UIDragItem] {
    return dragItems(at: indexPath)
}

func collectionView(_ collectionView: UICollectionView, itemsForBeginning session: UIDragSession, at indexPath: IndexPath) -> [UIDragItem] {
    session.localContext = collectionView
    return dragItems(at: indexPath)
}
func collectionView(_ collectionView: UICollectionView, canHandle session: UIDropSession) -> Bool {
    return session.canLoadObjects(ofClass: NSAttributedString.self)
}
func collectionView(_ collectionView: UICollectionView, dropSessionDidUpdate session: UIDropSession, withDestinationIndexPath destinationIndexPath: IndexPath?) -> UICollectionViewDropProposal {
    let isSelf = (session.localDragSession?.localContext as? UICollectionView) == collectionView
    return UICollectionViewDropProposal(operation: isSelf ? .move: .copy, intent: .insertAtDestinationIndexPath)
}
func collectionView(_ collectionView: UICollectionView, performDropWith coordinator: UICollectionViewDropCoordinator) {
    let destinationIndexPath = coordinator.destinationIndexPath ?? IndexPath(item: 0, section: 0)
    for item in coordinator.items {
        if let sourceIndexPath = item.sourceIndexPath{
            if let attributedString = item.dragItem.localObject as? NSAttributedString{
                collectionView.performBatchUpdates({numbersChoices.remove(at: sourceIndexPath.item)
                    numbersChoices.insert(attributedString.string, at: destinationIndexPath.item)
                    collectionView.deleteItems(at: [sourceIndexPath])
                    collectionView.insertItems(at: [destinationIndexPath])})
            }
            coordinator.drop(item.dragItem, toItemAt: destinationIndexPath)
        }else{
            let placeholderContext = coordinator.drop(item.dragItem, to: UICollectionViewDropPlaceholder(insertionIndexPath: destinationIndexPath, reuseIdentifier: "DropPlaceholderCell"))
            item.dragItem.itemProvider.loadObject(ofClass: NSAttributedString.self){(provider, error) in DispatchQueue.main.sync {
                if let attributedString = provider as? NSAttributedString{
                    placeholderContext.commitInsertion(dataSourceUpdates: {insertionIndexPath in
                        self.numbersChoices.insert(attributedString.string, at: insertionIndexPath.item)
                    })
                }else {
                    placeholderContext.deletePlaceholder()
                }
                }
            }
        }
    }
}
private func dragItems (at indexPath: IndexPath) -> [UIDragItem]{
    if let attributedString = (numberCollectionView.cellForItem(at: indexPath) as? numberCollectionViewCell)?.label.attributedText{
        let dragItem = UIDragItem(itemProvider: NSItemProvider(object: attributedString))
        dragItem.localObject = attributedString
        return [dragItem]
    }else {
        return []
    }
}
...
}

Here is the code for implementing drag and drop on the canvas:

class TrailingCanvas: UIView,UIDropInteractionDelegate {
var UserMatchingLocation = [CGFloat]()
var UILabelLocation = [CGFloat]()
 required init?(coder aDecoder: NSCoder) {
    super.init(coder:aDecoder)
    setup()
}
private func setup() {
    addInteraction(UIDropInteraction(delegate: self))
}
func dropInteraction(_ interaction: UIDropInteraction, canHandle session: UIDropSession) -> Bool {
    return session.canLoadObjects(ofClass: NSAttributedString.self)
}
func dropInteraction(_ interaction: UIDropInteraction, sessionDidUpdate session: UIDropSession) -> UIDropProposal {
    return UIDropProposal(operation: .copy)
}
func dropInteraction(_ interaction: UIDropInteraction, performDrop session: UIDropSession) {
    session.loadObjects(ofClass: NSAttributedString.self){
        providers in
        let dropPoint = session.location(in: self)
        for attributedString in providers as? [NSAttributedString] ?? []{
            self.addLabel(with: attributedString, centeredAt: dropPoint)
        }
    }
}
private func addLabel (with attributedString:NSAttributedString, centeredAt point: CGPoint){
    let label = UILabel()
    label.backgroundColor = .clear
    label.attributedText = attributedString
    label.sizeToFit()
    label.center = point
    addSubview(label)
    UILabelLocation.append(point.x)
    print(point.x)
}
...
}

Here is a screenshot of the main storyboard.
mainstoryboard

I have also created a repo on Github. Here is the link: https://github.com/Feanor007/TrailingTest-repo

Hi @feanor,
at first glance it explains that there is no convertPoint:fromLayer: function which is related to a layer. You need to debug your code to see where that is and fix it.

cheers,

would you please share some debugging skills? Like what commands can I use in the console to ask it output something useful, or what should I look for in the error report?

Many thanks

@feanor, I generally will start debugging by adding breakpoints to pin point where I believe my error is occurring or I may even type out print statements to let me know certain functions are running.

Best,
Gina

thanks. I think the problem could be in the clearCanvas function, but I don’t know how to fix it.

I think you are supposed to use removeFromSuperview() on each view you want to remove, not just set the sublayers array to nil.

   for subview in self.subviews {
        subview.removeFromSuperview()
    }

Thank you very much, it solves the problem of re-adding the UILabel. But it cannot erase the line I draw on the canvas now.

I don’t know for sure, but I would try removing the sublayers, after you have removed the subviews. Hopefully only the lines are left, and will get removed safely. Seems worth a try.

 func ClearCanvas () {
   for subview in self.subviews {
        subview.removeFromSuperview()
    }
    path.removeAllPoints()
    self.layer.sublayers = nil
    self.setNeedsDisplay()
}

Great that solves the problem! Thank you very much! By the way, I am curious about how to remove the line only, without removing the labels added.:thinking:

Depends on how you added the line, I think. If it is on its own layer, you could keep track of it in a variable, and just remove that layer from the sublayers, instead of all them.

yes, I figured out. Many thanks

This topic was automatically closed after 166 days. New replies are no longer allowed.