Search code examples
iosobjective-cuigesturerecognizeruisegmentedcontrol

Switching from UIScreenEdgePanGestureRecognizer to UISegmentedControl in Objective-C


I am a new objective-c developer. The purpose of the app being developed is to switch between 3 graphs each displaying scientific data. When the user drags their finger on these graphs, the data for that point is displayed. Currently, to switch between these three graphs, the UIScreenEdgePanGestureRecognizer was used. However, since Apple has got rid of this feature in recent updates, I want to use segmented controls to switch between the three graphs. I have been able to get the segmented controls to appear, however, I have not been able to get them to actually get the graphs to switch. I have attached the relavant parts of the (ORIGINAL) viewcontroller.m below. How would I go about this? Thanks.

For reference, the names of the three graphs are ts, ph, and pv.

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    /*
    // Show/hide nav bar
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(doubleTap)];
    
    [tap setNumberOfTapsRequired:2];
    [self.view addGestureRecognizer:tap];
    */
    
    touchHasRegistered = NO;
    allowQualityScrubbing = NO;
    shouldFineTune = 0;
    hasFineTuned = NO;
    
    [[UIApplication sharedApplication] setStatusBarHidden:YES];
    [self.navigationController setNavigationBarHidden:YES];
    
    [self.containerView addSubview:self.chartView];
    
    [self.view insertSubview:self.secondContainerView
                aboveSubview:self.containerView];
    
    [self.view insertSubview:self.infoView
                aboveSubview:self.secondContainerView];
    
    [self.containerView bringSubviewToFront:self.infoButton];
    
    [self.view setBackgroundColor:[UIColor whiteColor]];
    
    [self.chartView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.containerView);
    }];
    
    [self.infoView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.edges.equalTo(self.containerView);
    }];
    
    if (self.secondContainerView.superview != nil && self.chartView.image != nil) {
        [self.secondContainerView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.containerView).with.offset(20.0);
            make.top.equalTo(self.containerView).with.offset(20.0);
            make.height.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.height]);
            make.width.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.width]);
        }];
    }
    
    [self.secondContainerView addSubview:self.displayView];
    
    [self chooseNewFileWithChartType:self.chartView.chart.substanceType valueType:@"ts"];
    
    UIScreenEdgePanGestureRecognizer *rightRecog = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
                                                                                                     action:@selector(resetChart:)];
    [rightRecog setEdges:UIRectEdgeRight];
    [rightRecog setCancelsTouchesInView:YES];
    
    [self.chartView addGestureRecognizer:rightRecog];
    
    UIScreenEdgePanGestureRecognizer *leftRecog = [[UIScreenEdgePanGestureRecognizer alloc] initWithTarget:self
                                                                                                    action:@selector(resetChart:)];
    
    [leftRecog setEdges:UIRectEdgeLeft];
    [leftRecog setCancelsTouchesInView:YES];
    
    [self.chartView addGestureRecognizer:leftRecog];
}

- (void)viewWillAppear:(BOOL)animated
{
    [super viewWillAppear:animated];
    
    [self.view addSubview:self.popupView];
    [self.popupView mas_makeConstraints:^(MASConstraintMaker *make) {
        make.height.equalTo(@(self.popupView.frame.size.height));
        make.width.equalTo(@(self.popupView.frame.size.width));
        make.center.equalTo(self.view);
    }];
    /*
    // Add Adjuster Views
    NSSet *tags = [self tagsForAdjusterViews];
    
    CGFloat height = self.displayView.containerViewHeight/self.displayView.numberOfRows;
    
    for (id tag in tags) {
        RUAAdjusterView *adjusterView = [[RUAAdjusterView alloc] initWithFrame:CGRectZero
                                                                           tag:[(NSNumber *)tag integerValue]];
        adjusterView.delegate = self;
        [adjusterView setBackgroundColor:[UIColor clearColor]];
        [self.secondContainerView addSubview:adjusterView];
        [self.secondContainerView bringSubviewToFront:adjusterView];
        
        [adjusterView mas_makeConstraints:^(MASConstraintMaker *make) {
            make.left.equalTo(self.secondContainerView);
            make.right.equalTo(self.secondContainerView);
            make.top.equalTo([NSNumber numberWithFloat:(height*([(NSNumber *)tag floatValue] - 1) + self.displayView.containerViewOriginY + 2.0f)]);
            make.height.equalTo([NSNumber numberWithFloat:height - 4.0f]);
        }];
    }
     */
}

- (NSSet *)tagsForAdjusterViews
{
    return [NSSet setWithObjects:@1, @2, @6, @7, nil];
}

- (BOOL)prefersStatusBarHidden
{
    return YES;
}

#pragma mark - Lazy Init

- (LocationIndicatorImageView *)chartView
{
    if (!_chartView) {
        _chartView = (LocationIndicatorImageView *)[[LocationIndicatorImageView alloc] initWithFrame:self.containerView.frame
                                                                                               image:[UIImage imageNamed:@"Water_ts_chart.png"]
                                                                                              sender:self];
        
        [_chartView setChart:[RUChart chartWithChartType:@"ts"]];
    }
    return _chartView;
}

-(UIView *)displayView
{
    if (!_displayView) {
        _displayView = [[DisplayView alloc] initWithFrame:self.secondContainerView.frame];
        [_displayView setDataSource:self];
    }
    return _displayView;
}

-(UIView *)secondContainerView
{
    if (!_secondContainerView) {
        CGFloat height = 343.0f;
        CGFloat width = 225.0f;
        _secondContainerView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, width, height)];
    }
    return _secondContainerView;
}

-(UIImageView *)infoView
{
    if (!_infoView) {
        _infoView = [[UIImageView alloc] initWithFrame:CGRectZero];
        [_infoView setImage:[UIImage imageNamed:@"Legend.png"]];
        [_infoView setHidden:YES];
        [_infoView setUserInteractionEnabled:NO];
        [_infoView setBackgroundColor:[UIColor whiteColor]];
        
        UIView *container = [[UIView alloc] initWithFrame:CGRectMake(30, 30, 310, 310)];
        UITapGestureRecognizer *ytTap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                                action:@selector(showYoutubeVideo)];
        [ytTap setNumberOfTapsRequired:1];
        [container setUserInteractionEnabled:YES];
        [container addGestureRecognizer:ytTap];
        
        [_infoView addSubview:container];
        
        UIImageView *youtube = [[UIImageView alloc] initWithFrame:CGRectMake(0, 0, 60, 60)];
        [youtube setImage:[UIImage imageNamed:@"youtube.png"]];
        
        UITextView *textView1 = [[UITextView alloc] initWithFrame:CGRectMake(youtube.frame.origin.x + youtube.frame.size.width,
                                                                             youtube.frame.origin.y,
                                                                             250,
                                                                             youtube.frame.size.height/2.0)];
        UITextView *textView2 = [[UITextView alloc] initWithFrame:CGRectMake(youtube.frame.origin.x + youtube.frame.size.width,
                                                                             youtube.frame.origin.y + youtube.frame.size.height/2.0,
                                                                             250,
                                                                             youtube.frame.size.height/2.0)];
        UIFont *font = [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0];
        
        [textView1 setText:@"Learn about Thermodynamic"];
        [textView1 setFont:font];
        [textView1 setTextContainerInset:UIEdgeInsetsMake(11.0, 4.0, 4.0, 0.0)];
        [textView1 setUserInteractionEnabled:NO];
        
        [textView2 setText:@"Properties of Water"];
        [textView2 setFont:font];
        [textView2 setTextContainerInset:UIEdgeInsetsMake(0.0, 4.0, 0.0, 0.0)];
        [textView2 setUserInteractionEnabled:NO];
        
        [container addSubview:youtube];
        [container addSubview:textView1];
        [container addSubview:textView2];
        
        UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                              action:@selector(dismissInfo)];
        [tap setNumberOfTapsRequired:1];
        
        [_infoView addGestureRecognizer:tap];
    }
    return _infoView;
}

- (void)showYoutubeVideo
{
    [[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"http://www.youtube.com/watch?v=rJR-6OEw09k"]
                                       options:@{}
                             completionHandler:nil];
}

- (H2O_Wagner_Pruss *)wagPruss
{
    if (!_wagPruss) {
        _wagPruss = [[H2O_Wagner_Pruss alloc] initEOS];
    }
    return _wagPruss;
}

- (NSArray *)superheatedValues
{
    if (!_superheatedValues) {
        _superheatedValues = [[NSArray alloc] init];
    }
    return _superheatedValues;
}

- (NSArray *)superheatedKeys
{
    if (!_superheatedKeys) {
        _superheatedKeys = [[NSArray alloc] init];
    }
    return _superheatedKeys;
}

- (NSArray *)chartValueTypes
{
    if (!_chartValueTypes) {
        _chartValueTypes = [NSArray arrayWithObjects:@"ts",@"ph",@"pv", nil];
    }
    return _chartValueTypes;
}

- (RUAPopupView *)popupView
{
    if (!_popupView) {
        _popupView = [[RUAPopupView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 200.0f, 160.0f) text:@"t-s"];
    }
    return _popupView;
}

- (RUASpaceController *)spaceController
{
    if (!_spaceController) {
        _spaceController = [[RUASpaceController alloc] init];
        // NOTE: Seems like (10/8.0 and 10/9.0) and 20/30.0 felt best of ones I tried. Could use some refining.
        _spaceController.numPoints = 10;
        _spaceController.maxDiff = 7.0;
    }
    
    return _spaceController;
}

#pragma mark - Gesture Selectors

- (IBAction)displayInfo:(id)sender {
    [self.infoView setHidden:NO];
    [self.infoView setUserInteractionEnabled:YES];
}

-(void)dismissInfo
{
    [self.infoView setHidden:YES];
    [self.infoView setUserInteractionEnabled:NO];
}

-(void)doubleTap
{
    [self.popupView showHideAnimated:YES];
    /*
    if (self.navigationController.isNavigationBarHidden) {
        [self.navigationController setNavigationBarHidden:NO animated:YES];
        [[UIApplication sharedApplication] setStatusBarHidden:NO];
    } else {    
        [self.navigationController setNavigationBarHidden:YES animated:YES];
        [[UIApplication sharedApplication] setStatusBarHidden:YES];
    }
     */
}

- (void)resetChart:(UIScreenEdgePanGestureRecognizer *)recog
{
    [self.popupView.layer removeAllAnimations];
    if (recog.state == UIGestureRecognizerStateEnded) {
        NSInteger index = [self.chartValueTypes indexOfObject:self.chartView.chart.valueType];
        NSLog(@"%@, %@", self.chartValueTypes[((index+1)+3)%3], self.chartValueTypes[((index-1)+3)%3]);
        
        NSString *type;
        
        if (recog.edges == UIRectEdgeRight) {
            type = self.chartValueTypes[((index+1)+3)%3];
        } else if (recog.edges == UIRectEdgeLeft) {
            type = self.chartValueTypes[((index-1)+3)%3];
        }
        
        NSString *letter1 = [type substringToIndex:1];
        NSString *letter2 = [type substringFromIndex:1];
        
        NSString *displayName = [NSString stringWithFormat:@"%@-%@",letter1.uppercaseString,letter2];
        self.popupView.text = displayName;
        
        [self.chartView resetImage:[UIImage imageNamed:[NSString stringWithFormat:@"Water_%@_chart.png",type]]];
        self.chartView.chart = [RUChart chartWithChartType:type];
        [self inspectInfoButtonWithChartValueType:type];
        [self chooseNewFileWithChartType:self.chartView.chart.substanceType valueType:type];
        
        [self.secondContainerView mas_remakeConstraints:^(MASConstraintMaker *make) {
            make.top.equalTo(self.containerView).with.offset(20.0);
            make.height.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.height]);
            make.width.equalTo([NSNumber numberWithFloat:self.secondContainerView.frame.size.width]);
        }];
        
        if (self.chartView.chart.displayPosition == RUChartDisplayPositionLeft) {
            [self.secondContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.left.equalTo(self.containerView).with.offset(20.0);
            }];
        } else if (self.chartView.chart.displayPosition == RUChartDisplayPositionRight) {
            [self.secondContainerView mas_updateConstraints:^(MASConstraintMaker *make) {
                make.right.equalTo(self.containerView).with.offset(-20.0);
            }];
        }
        
        [self.popupView showHideAnimated:YES];
        
        if (touchHasRegistered) {
            if ([self.chartView.chart.valueType isEqualToString:@"ph"]) {
                if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentEnthalpy secondaryAxisValue:currentPressure]) {
                    [self.chartView moveMarkerToPrimaryAxisValue:currentEnthalpy
                                              secondaryAxisValue:currentPressure];
                } else {
                    [self.chartView removeMarker];
                }
            } else if ([self.chartView.chart.valueType isEqualToString:@"pv"]) {
                if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentSpecVolume secondaryAxisValue:currentPressure]) {
                    [self.chartView moveMarkerToPrimaryAxisValue:currentSpecVolume
                                              secondaryAxisValue:currentPressure];
                } else {
                    [self.chartView removeMarker];
                }
            } else if ([self.chartView.chart.valueType isEqualToString:@"ts"]) {
                if ([self.chartView pointIsWithinBoundsForPrimaryAxisValue:currentEntropy secondaryAxisValue:currentTemp]) {
                    [self.chartView moveMarkerToPrimaryAxisValue:currentEntropy
                                              secondaryAxisValue:currentTemp];
                } else {
                    [self.chartView removeMarker];
                }
            } else {
                touchHasRegistered = NO;
                [self.chartView removeMarker];
            }
        }
    }
}

Solution

  • UISegmentedControl look like a lot to setup, but actually is not.

    The following code just shows more detailed control in color and label positions inside the segments. If there is no further use somewhere else, there is no property needed to hold UISegmentedControl *. Call this once in -initWithFrame: or -viewDidLoad.

    - (void)setupSegmentCtrl {
        
        UISegmentedControl *segmentedCtrl = [[UISegmentedControl alloc] initWithItems:@[@"A",@"B",@"C"]];
        segmentedCtrl.momentary = YES;
        NSUInteger segItems = segmentedCtrl.numberOfSegments;
        segmentedCtrl.frame = CGRectMake(0, 0, 60*segItems, 40);
        
        // sorry - funny color scheme used to demonstrate
        segmentedCtrl.tintColor = UIColor.orangeColor;
        //segmentedCtrl.backgroundColor = UIColor.clearColor;
        UIColor *dark = [UIColor colorWithWhite:0.5 alpha:0.5];
        [segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:dark,NSForegroundColorAttributeName, [UIFont fontWithName:@"HelveticaNeue-Light" size:16.0],NSFontAttributeName, nil] forState:UIControlStateNormal];
        [segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:UIColor.redColor, NSForegroundColorAttributeName, nil] forState:UIControlStateSelected];
        [segmentedCtrl setTitleTextAttributes:[NSDictionary dictionaryWithObjectsAndKeys:UIColor.greenColor, NSForegroundColorAttributeName, nil] forState:UIControlStateHighlighted];
        
        // you can move the segments labels about some pixels with the following..
        //[segmentedCtrl setContentPositionAdjustment:UIOffsetMake(-1, -2) forSegmentType:UISegmentedControlSegmentAny barMetrics:UIBarMetricsDefault];
        
        // manually set an active index, .. as default
        [segmentedCtrl setSelectedSegmentIndex:0];
        
        [self.view addSubview:segmentedCtrl];
    
        // next lines work for all UIControls, setting a target and action manually. 
        // there are a lot UIControlEvent to choose/combine from available
        [segmentedCtrl addTarget:self action:@selector(segmentSelectedAction:) forControlEvents:UIControlEventValueChanged];
        
    }
    

    and as defined you will want a method that takes action when you touch the segments.

    -(void)segmentSelectedAction:(UISegmentedControl *)seg {
        NSLog(@"selectedSegmentIndex=%d", seg.selectedSegmentIndex);
        // what ever you gonna do with the seg.selectedSegmentIndex
    }
    

    and maybe good to know when using UISegmentedControl, when going in dark mode it has different color scheme for the background. So switch and test how it looks like.

    If you have to change layout cause of device rotates, you will have to expose UISegmentedControl *segmentCtrl as property or class variable and change frame and so on in -layoutSubviews to your needs.

    A last word to UIViews and UIGestureRecognizers. Sometime its much more practical to write your own UIView subclass and allocate that instead. Then you are able to use the following methods inside your subclass to catch touches directly.

    -(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
        [super touchesBegan:touches withEvent:event];
        for (UITouch *touch in touches) {
            NSLog(@"touchesBegan= %@",touch.description);
        }
    }
    // and the other possible..
    -(void)touchesMoved:withEvent:
    -(void)touchesEnded:withEvent:
    -(void)touchesCancelled:withEvent: