What is the best strategy for this?


Rick Aurbach
 

Problem: I am designing a collection view with overlapping cells. Each cell has an active area inside it (and the active areas of adjacent cells do not overlap). (More information about what I'm doing at the bottom of this post.) I want a tap in the active area of a cell to select that cell, but a tap outside the active area of any cell should deselect the currently selected cell [single selection only; I don't need to support multi-selection at this point].

My original thought was to override the collection view's indexPathForItem(at:) and either the cell's touch event handlers or UICollectionView's touch event handlers. But, to make this work, I also need to know something about HOW the touch event handler interact with the UICollectionView. (This might be as simple as having touchesEnded(_:with:) call selectItem(at:animated:scrollPosition:) and deselectItem(at:animated:). Or?)

Before I start possibly reinventing the wheel here, I'd like to ask if anyone has done anything similar and has any ideas to share.

Thanks,

Rick

-------------------------
Consider a square cell which contains a diamond. The four vertices of the diamond are the midpoints of the four sides of the cell. The active region of each cell is the area inside the diamond. Cells are close-packed horizontally. Each section (i.e., "row") is offset a half-width down and a half-width to the side of the cells above it. In other words, the cells are positioned so that the diamond edges are superimposed. The attached image should give you an idea.


Ben Kennedy
 

On 2 Apr 2021, at 3:27 pm, Rick Aurbach via groups.io <rlaurb=me.com@groups.io> wrote:

Consider a square cell which contains a diamond. The four vertices of the diamond are the midpoints of the four sides of the cell. The active region of each cell is the area inside the diamond. Cells are close-packed horizontally. Each section (i.e., "row") is offset a half-width down and a half-width to the side of the cells above it. In other words, the cells are positioned so that the diamond edges are superimposed. The attached image should give you an idea.
Perhaps this is naïve, but the first thing that comes to mind: could you not simply deal with it as a regular matrix of squares, and then apply a 45 degree transform on the view?

(I haven't tested anything like this, but I presume that touch events would be transformed accordingly…?)

-ben


Rick Aurbach
 

It's an interesting idea, but there's an added complication (which I apologize for not making explicit in the original post) which makes this problematic. Each of these objects is a cell in a UICollectionView (with a custom Layout). I rather fear that without overriding pieces of touch processing, the collection view will not treat the rotated cell geometry properly.

Another way of thinking of this problem is as trying to create a non-rectangular collection-view cell which responds based on its shape, not its bounding box.


Alex Zavatone
 

It would be interesting to try out.  Try removing the spaces between each cell and issue the rotation, then try it out and find out.

Alex Zavatone

On Apr 3, 2021, at 10:04 AM, Rick Aurbach via groups.io <rlaurb@...> wrote:

It's an interesting idea, but there's an added complication (which I apologize for not making explicit in the original post) which makes this problematic. Each of these objects is a cell in a UICollectionView (with a custom Layout). I rather fear that without overriding pieces of touch processing, the collection view will not treat the rotated cell geometry properly.

Another way of thinking of this problem is as trying to create a non-rectangular collection-view cell which responds based on its shape, not its bounding box.


Ben Kennedy
 

On Apr 3, 2021, at 8:04 AM, Rick Aurbach via groups.io <rlaurb=me.com@groups.io> wrote:

Each of these objects is a cell in a UICollectionView (with a custom Layout). I rather fear that without overriding pieces of touch processing, the collection view will not treat the rotated cell geometry properly.
Yes, so why not transform the collection view as a whole (not the cells)?

-ben


Ben Kennedy
 


On 3 Apr 2021, at 8:46 am, Ben Kennedy <ben-groups@...> wrote:

On Apr 3, 2021, at 8:04 AM, Rick Aurbach via groups.io <rlaurb@...> wrote:

Each of these objects is a cell in a UICollectionView (with a custom Layout). I rather fear that without overriding pieces of touch processing, the collection view will not treat the rotated cell geometry properly.

Yes, so why not transform the collection view as a whole (not the cells)?

This piqued my curiosity, so I couldn't help myself and built a test case. This seems to work perfectly -- and no collection view required. The code is short enough that I've copy/pasted it all below.

-ben


class ViewController: UIViewController {
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let dimension = 4
        let spacing = CGFloat(2.0)
        
        let containerStackView = UIStackView()
        containerStackView.axis = .vertical
        containerStackView.spacing = spacing
        containerStackView.alignment = .center
        containerStackView.distribution = .equalSpacing
        containerStackView.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(containerStackView)
        NSLayoutConstraint.activate([
            containerStackView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            containerStackView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
        ])
        
        for y in 1...(dimension * 2) {
            let rowStackView = UIStackView()
            rowStackView.axis = .horizontal
            rowStackView.spacing = spacing
            rowStackView.alignment = .center
            rowStackView.distribution = .equalSpacing
            rowStackView.translatesAutoresizingMaskIntoConstraints = false
            containerStackView.addArrangedSubview(rowStackView)
            
            for _ in 1...(y <= dimension ? y * 2 - 1 : (dimension * 2 - y + 1) * 2 - 1) {
                let cubbyHoleView = CubbyHoleView()
                cubbyHoleView.translatesAutoresizingMaskIntoConstraints = false
                rowStackView.addArrangedSubview(cubbyHoleView)
                NSLayoutConstraint.activate([
                    cubbyHoleView.widthAnchor.constraint(equalTo: view.widthAnchor, multiplier: 0.5 / CGFloat(dimension)),
                    cubbyHoleView.heightAnchor.constraint(equalTo: cubbyHoleView.widthAnchor)
                ])
            }
        }
        
        containerStackView.transform = CGAffineTransform.init(rotationAngle: -45.0 * CGFloat.pi / 180.0)
    }
}

class CubbyHoleView: UIView {
    let defaultBackgroundColor = UIColor.systemBlue.withAlphaComponent(0.1)
    init() {
        super.init(frame: .zero)
        backgroundColor = defaultBackgroundColor
        layer.borderColor = UIColor.black.cgColor
        layer.borderWidth = 2.0
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
        backgroundColor = .systemYellow
    }
    
    override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
        backgroundColor = defaultBackgroundColor
    }
}