Search code examples
iosmenu

circular menu - create menu & call action only if area is clicked & not just icon


I want to create circular menu like below for iPhone app.

enter image description here

I searched many libraries or solutions, however was unable to find the same. What all have is when they click on icon, menu action is called, however I want to call action if I click anywhere on the button as shown below.

enter image description here

User action will only get called if I click anywhere in the user of user as shown in above image.

Any idea how to achieve this?

I have one idea as checking the x & y position of the tap and findout where its clicked (but this is very time costly)

OR

Is there any way to create buttons of these shapes?


Solution

  • I have one idea as checking the x & y position of the tap and findout where its clicked (but this is very time costly)

    That's actually a very good idea, and the calculations are not very costly at all.

    // Function to compute button touched
    //
    // Returns:
    // 0 - 7:  One of the 8 outer buttons touched
    // 8:      Center button touched
    // 9:      Touch outside of outer circle (no button touched)
    
    func detectTouchedButton(_ touchPoint: CGPoint) -> Int {
    
        // Set these values for your situation:
        let innerRadius: CGFloat = 50
        let outerRadius: CGFloat = 150
        let center = CGPoint(x: 150, y: 200)
    
        // Compute horizontal and vertical distance of touch from center
        let dx = touchPoint.x - center.x
        let dy = center.y - touchPoint.y
    
        // was the center button touched?
        let distanceSquared = dx * dx + dy * dy
        if distanceSquared < innerRadius * innerRadius {
            return 8
        }
    
        // was touch outside of outer circle?
        if distanceSquared > outerRadius * outerRadius {
            return 9
        }
    
        // Use atan2 to get an angle between -.pi and
        // .pi.  Divide by .pi to get number in the range
        // of (-1...1).  Add 1 to get a range of (0...2).
        // Multiply by 4 to get a range of (0...8).  Mod
        // by 8 since 0 == 8 (same button)  
        return Int(4 * (atan2(dy, dx) / .pi + 1)) % 8
    }
    

    Notes:

    • I flipped the dy calculation because the coordinate system is upside down. This just returns the coordinate system to the +y is up mathematical sense and changes the final ordering of 0 through 7.

    • If you wanted, you could modify this to return an Int? and then return nil for touches that aren't on a button.