3

I am attempting to use a UISegmentedControl to control the sort order of my table, in particular I want to register a tap on a segment that is already selected (to inverse my sort). As such, I am trying to raise an UIControlEvent.ValueChanged event whenever any segment is tapped.

I have overridden SelectedSegment from UISegmentedControl and am using this custom class for my element in xcode, however, it appears as though this code is not called when the the Segment control is tapped (It is called when the view is loaded though =S).

[Register("ReselectableSegment")]
public class ReselectableSegment : UISegmentedControl
{
    static Selector selInit       = new Selector("init");  

    [Export("init")]  
    public ReselectableSegment()  
        : base(NSObjectFlag.Empty)  
    {  
        Handle = Messaging.IntPtr_objc_msgSend(this.Handle,  
            selInit.Handle);  
    }  

    public ReselectableSegment (IntPtr handle) : base(handle){}

    public override int SelectedSegment
    {
        [Export ("SelectedSegment:")]
        set { 
            if(this.SelectedSegment == value)
                this.SendActionForControlEvents(UIControlEvent.ValueChanged);

            base.SelectedSegment = value;
        }
    }
}

This question has been asked before at: UISegmentedControl register taps on selected segment, however that question is for ObjC and this question is for C#/Monotouch.

Community
  • 1
  • 1
Symmetry
  • 257
  • 2
  • 10

2 Answers2

2

The last answer of the question link you provided contains the best (well working) answer (I upvoted it, you should too ;-).

note: from the comments it looks like the previous answers were (maybe) working on iOS4 but not on iOS5.

Here's the code to do what you're looking for. I used the RectangleF .ctor to test it, you might have to add your own .ctor if you're using something else (but it's not as complex as your original code, e.g. no selector should be needed to subclass this).

    class MyUISegmentedControl : UISegmentedControl {

        public MyUISegmentedControl (RectangleF r) : base (r) {}

        public override void TouchesBegan (NSSet touches, UIEvent evt)
        {
            int current = SelectedSegment;
            base.TouchesBegan (touches, evt);
            if (current == SelectedSegment)
                SendActionForControlEvents (UIControlEvent.ValueChanged);
        }
    }
Community
  • 1
  • 1
poupou
  • 43,413
  • 6
  • 77
  • 174
  • Thanks poupou. Sorry it took me so long to get around to marking this as the answer, I got sidetracked into doing different things. Your response has helped a lot! – Symmetry Dec 20 '11 at 04:00
  • Note to self ... "last answer of the question link" is ambiguous and a moving target. Best to provide a link to the answer, rather than the question. – Travelling Man Jun 09 '17 at 05:19
0

The links provided have good answers for iOS7 but using Objective C/Swift and specifically for deselecting a segment when the currently selected one was pressed again.

Here is the C# version which needs to use the TouchesEnded event instead of TouchesBegan. Using TouchesBegan fired the UISegmentedControl.ValueChanged event twice for me which brought about incorrect behaviour.

This code includes handling of when the segment is touched and dragged off the control to cancel the event.

 class OptionalSegmentedControl : UISegmentedControl
{


    public override void TouchesEnded(NSSet touches, UIEvent evt)
    {

        // Getting touch information to work out whether segment has been pressed, then dragged off to cancel the event
        // Comment these lines out if you don't care about handling this
        CGPoint locationPoint = ((UITouch)touches.AnyObject).LocationInView(this);
        CGPoint viewPoint = this.ConvertPointFromView(locationPoint, this);

        // Save the selected segment first
        nint current = this.SelectedSegment;

        // then fire the event
        base.TouchesEnded(touches, evt);


        // Check if the touch point is still on the control when the touch has ended, 
        // as well as comparing the current and new selected segment values 
        // and then fire the ValueChanged event if the same segment is pressed


        // Use this line and comment the second if statement if you don't care about handling click and drag to cancel the event 
        //if (current == this.SelectedSegment)
        if ((this.PointInside(viewPoint, evt)) && (current == this.SelectedSegment))
        {
            this.SelectedSegment = -1;
            SendActionForControlEvents(UIControlEvent.ValueChanged);
        }
    }


}

Then in the ValueChanged event you do what you need to do with the UISegmentedControl:

            // cell.SegmentedControl for me was a UISegmented control in a UITableViewCell
            cell.SegmentedControl.ValueChanged += (s, e) =>
            {
                OptionalSegmentedControl osc = (OptionalSegmentedControl)s;

               // continue with your code logic.....

            };

One thing to note is that I started off with the TouchesBegan void, but then changed the name to TouchesEnded when I realised that's what was needed for iOS7 and above. Still didn't work until I realised I had to also change the line:

base.TouchesEnded(touches, evt);

So don't forget to change that if your converting from TouchesBegan to TouchesEnded.

Tahari
  • 131
  • 6