element-ios/Vector/ViewController/SegmentedViewController.m

498 lines
21 KiB
Mathematica
Raw Normal View History

/*
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"
#import "RageShakeManager.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)destroy
{
for (id viewController in viewControllers)
{
if ([viewController respondsToSelector:@selector(destroy)])
{
[viewController destroy];
}
}
viewControllers = nil;
sectionTitles = nil;
sectionLabels = nil;
if (selectedMarkerView)
{
[selectedMarkerView removeFromSuperview];
selectedMarkerView = nil;
}
[super destroy];
}
- (void)setSelectedIndex:(NSUInteger)selectedIndex
{
if (_selectedIndex != selectedIndex)
{
_selectedIndex = selectedIndex;
[self displaySelectedViewController];
}
}
- (NSArray<UIViewController *> *)viewControllers
{
return viewControllers;
}
#pragma mark -
- (void)viewDidLoad
{
[super viewDidLoad];
// Setup `MXKViewControllerHandling` properties
self.defaultBarTintColor = kVectorNavBarTintColor;
self.enableBarTintColorStatusChange = NO;
self.rageShakeManager = [RageShakeManager sharedManager];
// 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
[NSLayoutConstraint deactivateConstraints:@[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];
[NSLayoutConstraint activateConstraints:@[self.selectionContainerTopConstraint]];
[self createSegmentedViews];
}
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
if (_selectedViewController)
{
2016-01-29 17:16:30 +00:00
// Make iOS invoke child viewWillAppear
[_selectedViewController beginAppearanceTransition:YES animated:animated];
2016-01-29 17:16:30 +00:00
}
}
- (void)viewWillDisappear:(BOOL)animated
{
[super viewWillDisappear:animated];
if (_selectedViewController)
{
// Make iOS invoke child viewWillDisappear
[_selectedViewController beginAppearanceTransition:NO animated:animated];
}
}
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
if (_selectedViewController)
{
// Make iOS invoke child viewDidAppear
[_selectedViewController endAppearanceTransition];
}
}
- (void)viewDidDisappear:(BOOL)animated
{
[super viewDidDisappear:animated];
if (_selectedViewController)
{
// Make iOS invoke child viewDidDisappear
[_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 = kVectorColorGreen;
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
[NSLayoutConstraint activateConstraints:@[leftConstraint, rightConstraint, topConstraint, 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
{
// Sanity check
NSAssert(sectionLabels.count, @"[SegmentedViewController] addSelectedMarkerView failed - At least one view controller is required");
// create the selected marker view
selectedMarkerView = [[UIView alloc] init];
selectedMarkerView.backgroundColor = kVectorColorGreen;
[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
[NSLayoutConstraint activateConstraints:@[leftMarkerViewConstraint, widthConstraint, bottomConstraint, heightConstraint]];
}
- (void)displaySelectedViewController
{
// Sanity check
NSAssert(sectionLabels.count, @"[SegmentedViewController] displaySelectedViewController failed - At least one view controller is required");
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];
[NSLayoutConstraint deactivateConstraints:@[displayedVCTopConstraint, displayedVCLeftConstraint, displayedVCWidthConstraint, displayedVCHeightConstraint]];
}
UILabel* label = [sectionLabels objectAtIndex:_selectedIndex];
label.font = [UIFont boldSystemFontOfSize:17];
// update the marker view position
[NSLayoutConstraint deactivateConstraints:@[leftMarkerViewConstraint]];
leftMarkerViewConstraint = [NSLayoutConstraint constraintWithItem:selectedMarkerView
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:[sectionLabels objectAtIndex:_selectedIndex]
attribute:NSLayoutAttributeLeading
multiplier:1.0
constant:0];
[NSLayoutConstraint activateConstraints:@[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];
displayedVCLeftConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view
attribute:NSLayoutAttributeLeading
relatedBy:NSLayoutRelationEqual
toItem:self.viewControllerContainer
attribute:NSLayoutAttributeLeading
multiplier:1.0f
constant:0.0f];
displayedVCWidthConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view
attribute:NSLayoutAttributeWidth
relatedBy:NSLayoutRelationEqual
toItem:self.viewControllerContainer
attribute:NSLayoutAttributeWidth
multiplier:1.0
constant:0];
displayedVCHeightConstraint = [NSLayoutConstraint constraintWithItem:_selectedViewController.view
attribute:NSLayoutAttributeHeight
relatedBy:NSLayoutRelationEqual
toItem:self.viewControllerContainer
attribute:NSLayoutAttributeHeight
multiplier:1.0
constant:0];
[NSLayoutConstraint activateConstraints:@[displayedVCTopConstraint, displayedVCLeftConstraint, displayedVCWidthConstraint, 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
self.selectedIndex = pos;
}
}
@end