1

I tried to create custom segmented control which should change its look if you are hovering it. But it does not work as I expected.

Here's the code (I am sorry that it is that long but I already made it 200 lines shorter then it was):

class MySegmentedControl: UIView {
    var state = MySegmentedControl_State.unknown
    var buttons = [MySegmentedControl_ButtonView]()
    func setUpView(){
        let view = UIView(frame: self.frame)
        view.frame.origin.x = 0
        view.frame.origin.y = 0
        view.addSubview(holderView())
        self.addSubview(view)
    }
    func holderView() -> UIView{
        let view = UIView(frame: CGRect(origin: CGPoint(x: 100, y: 0), size: CGSize(width: 490, height: 70)))
        view.layer.borderWidth = 5
        view.layer.borderColor = UIColor.gray.cgColor
        view.layer.cornerRadius = view.frame.height / 2
        view.layer.masksToBounds = true
        view.clipsToBounds = true
        view.backgroundColor = #colorLiteral(red: 0.5741485357, green: 0.5741624236, blue: 0.574154973, alpha: 1)
        //place the first button
        let button_1 = MySegmentedControl_ButtonView(text: "One", frame: CGRect(x: 0, y: 0, width: 163.3333, height: 70), type: .one, delegate: self)
        //place the second Button
        let button_2 = MySegmentedControl_ButtonView(text: "Two", frame: CGRect(x: 163.3333, y: 0, width: 163.3333, height: 70), type: .two, delegate: self)
        //place the third Button
        let button_3 = MySegmentedControl_ButtonView(text: "Three", frame: CGRect(x: 163.3333*2, y: 0, width: 163.3333, height: 70), type: .three, delegate: self)
        buttons.append(button_1); buttons.append(button_2); buttons.append(button_3)
        view.addSubview(button_1)
        view.addSubview(button_2)
        view.addSubview(button_3)
        
        return view
    }
    
    
    class MySegmentedControl_ButtonView: UIView{
        private var selected = false
        private var hovering = false
        var text = ""
        private var type: MySegmentedControl_State
        private var delegate: MySegmentedControl_ButtonView_Delegate
        private var label = UILabel()
        func setUpView(){
            layer.cornerRadius = frame.height/2
            let label = UILabel(frame: frame)
            self.label = label
            label.tintColor = .white
            label.text = text
            label.sizeToFit()
            label.center = self.center
            label.font =  label.font.withSize(15)
            let regionizer = UIHoverGestureRecognizer(target: self, action: #selector(didHover(_:)))
            addGestureRecognizer(regionizer)
            addSubview(label)
            //set up the button
            let button = UIButton(frame: bounds, primaryAction: UIAction(handler: { [self] action in
                selected = true
                delegate.shouldChangeState(to: type)
                delegate.shouldDeselectOthers(without: text)
                if !hovering{
                    backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.8)
                }else{
                    self.backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.9435433103)
                }
                
            }))
            addSubview(button)
        }
        
        func deselect(){
            self.selected = false
            if hovering{
                backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
                UIView.animate(withDuration: 0.2) {[self]in
                    label.frame.origin.y = 10
                    label.font =  label.font.withSize(12)
                    
                }
            }else{
                backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
                UIView.animate(withDuration: 0.2) {[self]in
                    label.frame.origin.y = 10
                    label.font =  label.font.withSize(12)
                    
                }
            }
        }
        
        @objc
        func didHover(_ recognizer: UIHoverGestureRecognizer){
            switch recognizer.state{
            case .began, .changed:
                hovering = true
                if selected{
                    backgroundColor = #colorLiteral(red: 0.7540688515, green: 0.7540867925, blue: 0.7540771365, alpha: 0.15)
                    UIView.animate(withDuration: 0.2) {[self]in
                        label.frame.origin.y = 10
                        label.font =  label.font.withSize(12)
                        
                    }
                }else{
                    backgroundColor = #colorLiteral(red: 0.2605174184, green: 0.2605243921, blue: 0.260515637, alpha: 0.15)
                    UIView.animate(withDuration: 0.2) {[self]in
                        label.frame.origin.y = 10
                        label.font =  label.font.withSize(12)
                        
                    }
                }
                
            case .ended:
                hovering = false
                if selected{
                    self.backgroundColor = #colorLiteral(red: 0.9961533629, green: 0.9931518435, blue: 1, alpha: 0.2)
                    UIView.animate(withDuration: 0.2) {[self]in
                        label.center.y = center.y
                        label.font =  label.font.withSize(15)
                        
                    }
                }else{
                    self.backgroundColor = .clear
                    UIView.animate(withDuration: 0.2) {[self]in
                        label.center.y = center.y
                        label.font =  label.font.withSize(15)
                        
                    }
                }
            default:break
            }
        }
        
       
        
        init(text: String, frame: CGRect, type: MySegmentedControl_State, delegate: MySegmentedControl_ButtonView_Delegate){
            
            self.type = type
            self.delegate = delegate
            super.init(frame: frame)
            self.text = text
            setUpView()
        }
        
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    }
    
}

extension MySegmentedControl: MySegmentedControl_ButtonView_Delegate{
    func shouldDeselectOthers(without: String) {
        for button in buttons{
            if button.text != without{
                button.deselect()
            }
        }
    }
    
    func shouldChangeState(to state: MySegmentedControl_State) {
        self.state = state
    }
    
    
}

protocol MySegmentedControl_ButtonView_Delegate{
    func shouldDeselectOthers(without: String)
    func shouldChangeState(to state: MySegmentedControl_State)
}


enum MySegmentedControl_State: String{
    case unknown = "Non specific case avalaible"
    case one = "One selected"
    case two = "Two selected"
    case three = "Three selected"
}

But the second of my buttons is displayed as the third and the the third does not get displayed, but if I hover the place where button two SHOULD be, it still gets hovered.

Here is a shout video:

Video showing problems with this code

My App is running on MacOS with MacCatalyst

2
  • Is there a reason you are using explicit frames instead of auto-layout? Commented Nov 29, 2022 at 21:11
  • I am new to swift and I saw it in the Internet like that. Commented Nov 30, 2022 at 14:53

1 Answer 1

1

In setUpView() inside class MySegmentedControl_ButtonView, you are trying to set the label's position with:

label.center = self.center

However, self.center is relative to self's superview. So your labels are being shifted.

If you change that line to:

label.center = CGPoint(x: self.bounds.midX, y: self.bounds.midY)

the label centering will be correct.

As I asked in my comment though... why aren't you using auto-layout? It would make things much easier (and much more flexible).

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.