/* Copyright 2015 OpenMarket Ltd Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ #import "SegmentedViewController.h" #import "VectorDesignValues.h" @interface SegmentedViewController () { // list of displayed UIViewControllers NSArray* viewControllers; // displayed viewController NSLayoutConstraint *displayedVCTopConstraint; NSLayoutConstraint *displayedVCLeftConstraint; NSLayoutConstraint *displayedVCWidthConstraint; NSLayoutConstraint *displayedVCHeightConstraint; // list of NSString NSArray* sectionTitles; // list of section labels NSArray* sectionLabels; // the selected marker view UIView* selectedMarkerView; NSLayoutConstraint *leftMarkerViewConstraint; } @end @implementation SegmentedViewController #pragma mark - Class methods + (UINib *)nib { return [UINib nibWithNibName:NSStringFromClass([SegmentedViewController class]) bundle:[NSBundle bundleForClass:[SegmentedViewController class]]]; } + (instancetype)segmentedViewController { return [[[self class] alloc] initWithNibName:NSStringFromClass([SegmentedViewController class]) bundle:[NSBundle bundleForClass:[SegmentedViewController class]]]; } /** init the segmentedViewController with a list of UIViewControllers. @param titles the section tiles @param viewControllers the list of viewControllers to display. @param defaultSelected index of the default selected UIViewController in the list. */ - (void)initWithTitles:(NSArray*)titles viewControllers:(NSArray*)someViewControllers defaultSelected:(NSUInteger)index { viewControllers = someViewControllers; sectionTitles = titles; _selectedIndex = index; } - (void)setSelectedIndex:(NSUInteger)selectedIndex { if (_selectedIndex != selectedIndex) { _selectedIndex = selectedIndex; [self displaySelectedViewController]; } } #pragma mark - - (void)addConstraint:(UIView*)view constraint:(NSLayoutConstraint*)aConstraint { // sanity check if (view && aConstraint) { if ([NSLayoutConstraint respondsToSelector:@selector(activateConstraints:)]) { [NSLayoutConstraint activateConstraints:@[aConstraint]]; } else { [view addConstraint:aConstraint]; } } } - (void)removeConstraint:(UIView*)view constraint:(NSLayoutConstraint*)aConstraint { // sanity check if (view && aConstraint) { if ([NSLayoutConstraint respondsToSelector:@selector(deactivateConstraints:)]) { [NSLayoutConstraint deactivateConstraints:@[aConstraint]]; } else { [view removeConstraint:aConstraint]; } } } - (void)viewDidLoad { [super viewDidLoad]; // Check whether the view controller has been pushed via storyboard if (!self.viewControllerContainer) { // Instantiate view controller objects [[[self class] nib] instantiateWithOwner:self options:nil]; } // Adjust Top [self removeConstraint:self.view constraint:self.selectionContainerTopConstraint]; // it is not possible to define a constraint to the topLayoutGuide in the xib editor // so do it in the code .. self.selectionContainerTopConstraint = [NSLayoutConstraint constraintWithItem:self.topLayoutGuide attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]; [self addConstraint:self.selectionContainer constraint:self.selectionContainerTopConstraint]; [self createSegmentedViews]; } - (void)viewWillAppear:(BOOL)animated { [super viewWillAppear:animated]; if (_selectedViewController) { // Make iOS invoke child viewWillAppear and viewDidAppear [_selectedViewController beginAppearanceTransition:YES animated:animated]; [_selectedViewController endAppearanceTransition]; } } - (void)createSegmentedViews { NSMutableArray* labels = [[NSMutableArray alloc] init]; NSUInteger count = viewControllers.count; for(NSUInteger index = 0; index < count; index++) { // create programmatically each label UILabel *label = [[UILabel alloc] init]; label.text = [sectionTitles objectAtIndex:index]; label.font = [UIFont systemFontOfSize:17]; label.textAlignment = NSTextAlignmentCenter; label.textColor = VECTOR_GREEN_COLOR; label.backgroundColor = [UIColor clearColor]; // the constraint defines the label frame // so ignore any autolayout stuff [label setTranslatesAutoresizingMaskIntoConstraints:NO]; // add the label before setting the constraints [self.selectionContainer addSubview:label]; NSLayoutConstraint *leftConstraint; if (labels.count) { leftConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:[labels objectAtIndex:(index-1)] attribute:NSLayoutAttributeTrailing multiplier:1.0 constant:0]; } else { leftConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; } NSLayoutConstraint *rightConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeWidth multiplier:1.0 / count constant:0]; NSLayoutConstraint *topConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeTop multiplier:1.0 constant:0]; NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:label attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]; // set the constraints if ([NSLayoutConstraint respondsToSelector:@selector(activateConstraints:)]) { [NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, heightConstraint]]; } else { [self.selectionContainer addConstraint:leftConstraint]; [self.selectionContainer addConstraint:rightConstraint]; [self.selectionContainer addConstraint:topConstraint]; [label addConstraint:heightConstraint]; } UITapGestureRecognizer *labelTapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(onLabelTouch:)]; [labelTapGesture setNumberOfTouchesRequired:1]; [labelTapGesture setNumberOfTapsRequired:1]; label.userInteractionEnabled = YES; [label addGestureRecognizer:labelTapGesture]; [labels addObject:label]; } sectionLabels = labels; [self addSelectedMarkerView]; [self displaySelectedViewController]; } - (void)addSelectedMarkerView { // create the selected marker view selectedMarkerView = [[UIView alloc] init]; selectedMarkerView.backgroundColor = VECTOR_GREEN_COLOR; [selectedMarkerView setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.selectionContainer addSubview:selectedMarkerView]; leftMarkerViewConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:[sectionLabels objectAtIndex:_selectedIndex] attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; NSLayoutConstraint *widthConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeWidth multiplier:1.0 / sectionLabels.count constant:0]; NSLayoutConstraint *bottomConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:self.selectionContainer attribute:NSLayoutAttributeBottom multiplier:1.0 constant:0]; NSLayoutConstraint *heightConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.0 constant:3]; // set the constraints if ([NSLayoutConstraint respondsToSelector:@selector(activateConstraints:)]) { [NSLayoutConstraint activateConstraints:@[leftMarkerViewConstraint, widthConstraint, bottomConstraint, heightConstraint]]; } else { [self.selectionContainer addConstraint:leftMarkerViewConstraint]; [self.selectionContainer addConstraint:bottomConstraint]; [selectedMarkerView addConstraint:heightConstraint]; [selectedMarkerView addConstraint:heightConstraint]; } } - (void)displaySelectedViewController { if (_selectedViewController) { NSUInteger index = [viewControllers indexOfObject:_selectedViewController]; if (index != NSNotFound) { UILabel* label = [sectionLabels objectAtIndex:index]; label.font = [UIFont systemFontOfSize:17]; } [_selectedViewController.view removeFromSuperview]; [_selectedViewController removeFromParentViewController]; [self removeConstraint:_selectedViewController.view constraint:displayedVCWidthConstraint]; [self removeConstraint:_selectedViewController.view constraint:displayedVCHeightConstraint]; [self removeConstraint:self.viewControllerContainer constraint:displayedVCTopConstraint]; [self removeConstraint:self.viewControllerContainer constraint:displayedVCLeftConstraint]; } UILabel* label = [sectionLabels objectAtIndex:_selectedIndex]; label.font = [UIFont boldSystemFontOfSize:17]; // update the marker view position [self removeConstraint:selectedMarkerView constraint:leftMarkerViewConstraint]; leftMarkerViewConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:[sectionLabels objectAtIndex:_selectedIndex] attribute:NSLayoutAttributeLeading multiplier:1.0 constant:0]; [self addConstraint:selectedMarkerView constraint:leftMarkerViewConstraint]; // Set the new selected view controller _selectedViewController = [viewControllers objectAtIndex:_selectedIndex]; // Make iOS invoke child viewWillAppear [_selectedViewController beginAppearanceTransition:YES animated:YES]; [self addChildViewController:_selectedViewController]; [_selectedViewController.view setTranslatesAutoresizingMaskIntoConstraints:NO]; [self.viewControllerContainer addSubview:_selectedViewController.view]; displayedVCTopConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:self.viewControllerContainer attribute:NSLayoutAttributeTop multiplier:1.0f constant:0.0f]; [self addConstraint:self.viewControllerContainer constraint:displayedVCTopConstraint]; displayedVCLeftConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view attribute:NSLayoutAttributeLeading relatedBy:NSLayoutRelationEqual toItem:self.viewControllerContainer attribute:NSLayoutAttributeLeading multiplier:1.0f constant:0.0f]; [self addConstraint:self.viewControllerContainer constraint:displayedVCLeftConstraint]; displayedVCWidthConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationEqual toItem:self.viewControllerContainer attribute:NSLayoutAttributeWidth multiplier:1.0 constant:0]; [self addConstraint:_selectedViewController.view constraint:displayedVCWidthConstraint]; displayedVCHeightConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:self.viewControllerContainer attribute:NSLayoutAttributeHeight multiplier:1.0 constant:0]; [self addConstraint:_selectedViewController.view constraint:displayedVCHeightConstraint]; [_selectedViewController didMoveToParentViewController:self]; [_selectedViewController endAppearanceTransition]; // refresh the navbar background color // to display if the homeserver is reachable. [self onMatrixSessionChange]; } #pragma mark - Search - (void)showSearch:(BOOL)animated { [super showSearch:animated]; // Show the tabs header if (animated) { [UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ self.selectionContainerHeightConstraint.constant = 44; [self.view layoutIfNeeded]; } completion:^(BOOL finished){ }]; } else { self.selectionContainerHeightConstraint.constant = 44; [self.view layoutIfNeeded]; } } - (void)hideSearch:(BOOL)animated { [super hideSearch:animated]; // Hide the tabs header if (animated) { [UIView animateWithDuration:.3 delay:0 options:UIViewAnimationOptionBeginFromCurrentState | UIViewAnimationOptionCurveEaseIn animations:^{ self.selectionContainerHeightConstraint.constant = 0; [self.view layoutIfNeeded]; } completion:^(BOOL finished) { // Go back to the main tab // Do it at the end of the animation when the tabs header of the SegmentedVC is hidden // so that the user cannot see the selection bar of this header moving self.selectedIndex = 0; self.selectedViewController.view.hidden = NO; }]; } else { self.selectionContainerHeightConstraint.constant = 0; [self.view layoutIfNeeded]; // Go back to the recents tab self.selectedIndex = 0; self.selectedViewController.view.hidden = NO; } } #pragma mark - touch event - (void)onLabelTouch:(UIGestureRecognizer*)gestureRecognizer { NSUInteger pos = [sectionLabels indexOfObject:gestureRecognizer.view]; // check if there is an update before triggering anything if ((pos != NSNotFound) && (_selectedIndex != pos)) { // update the selected index _selectedIndex = pos; [self displaySelectedViewController]; } } @end