mirror of
https://github.com/material-components/material-components-ios.git
synced 2026-02-20 08:27:32 +08:00
337 lines
11 KiB
Objective-C
337 lines
11 KiB
Objective-C
// Copyright 2016-present the Material Components for iOS authors. All Rights Reserved.
|
|
//
|
|
// 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 "MaterialColorScheme.h"
|
|
#import "MaterialPalettes.h"
|
|
#import "MaterialSlider+ColorThemer.h"
|
|
#import "MaterialSlider.h"
|
|
#import "MaterialTypographyScheme.h"
|
|
|
|
static NSString *const kReusableIdentifierItem = @"sliderItemCellIdentifier";
|
|
static CGFloat const kSliderHorizontalMargin = 16;
|
|
static CGFloat const kSliderVerticalMargin = 12;
|
|
static CGFloat const kSliderMinimumTouchSize = 48;
|
|
|
|
@interface MDCSliderModel : NSObject
|
|
|
|
@property(nonatomic, copy) NSString *labelString;
|
|
@property(nonatomic, assign) UIColor *labelColor;
|
|
@property(nonatomic, assign) UIColor *bgColor;
|
|
@property(nonatomic, nullable) UIColor *sliderColor;
|
|
@property(nonatomic, nullable) UIColor *filledTickColor;
|
|
@property(nonatomic, nullable) UIColor *backgroundTickColor;
|
|
@property(nonatomic, nullable) UIColor *trackBackgroundColor;
|
|
@property(nonatomic, assign) int numDiscreteValues;
|
|
@property(nonatomic, assign) CGFloat value;
|
|
@property(nonatomic, assign) CGFloat anchorValue;
|
|
@property(nonatomic, assign) BOOL discreteValueLabel;
|
|
@property(nonatomic, assign) BOOL hollowCircle;
|
|
@property(nonatomic, assign) BOOL enabled;
|
|
@property(nonatomic, assign) BOOL hapticsEnabled;
|
|
@property(nonatomic, assign) BOOL shouldEnableHapticsForAllDiscreteValues;
|
|
|
|
- (void)didChangeMDCSliderValue:(MDCSlider *)slider;
|
|
|
|
@end
|
|
|
|
@implementation MDCSliderModel
|
|
|
|
- (instancetype)init {
|
|
if (self = [super init]) {
|
|
// Default values
|
|
_labelString = @"";
|
|
_labelColor = [UIColor blackColor];
|
|
_bgColor = [UIColor whiteColor];
|
|
_sliderColor = nil;
|
|
_trackBackgroundColor = nil;
|
|
_numDiscreteValues = 0;
|
|
_value = 0.5;
|
|
_anchorValue = -CGFLOAT_MAX;
|
|
_discreteValueLabel = YES;
|
|
_hollowCircle = YES;
|
|
_enabled = YES;
|
|
_hapticsEnabled = YES;
|
|
_shouldEnableHapticsForAllDiscreteValues = NO;
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)didChangeMDCSliderValue:(MDCSlider *)slider {
|
|
NSLog(@"did change %@ value: %f", NSStringFromClass([slider class]), slider.value);
|
|
_value = slider.value;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface MDCSliderExampleCollectionViewCell : UICollectionViewCell
|
|
@property(nonatomic, strong, nullable) UIFont *labelFont;
|
|
- (void)applyModel:(MDCSliderModel *)model withColorScheme:(MDCSemanticColorScheme *)colorScheme;
|
|
@end
|
|
|
|
@implementation MDCSliderExampleCollectionViewCell {
|
|
UILabel *_label;
|
|
MDCSlider *_slider;
|
|
}
|
|
|
|
- (instancetype)initWithFrame:(CGRect)frame {
|
|
if (self = [super initWithFrame:frame]) {
|
|
_label = [[UILabel alloc] init];
|
|
[self.contentView addSubview:_label];
|
|
|
|
_slider = [[MDCSlider alloc] initWithFrame:CGRectZero];
|
|
[self.contentView addSubview:_slider];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void)applyModel:(MDCSliderModel *)model withColorScheme:(MDCSemanticColorScheme *)colorScheme {
|
|
_label.text = model.labelString;
|
|
_label.textColor = model.labelColor;
|
|
self.contentView.backgroundColor = model.bgColor;
|
|
_slider.statefulAPIEnabled = YES;
|
|
[MDCSliderColorThemer applySemanticColorScheme:colorScheme toSlider:_slider];
|
|
_slider.numberOfDiscreteValues = model.numDiscreteValues;
|
|
_slider.value = model.value;
|
|
_slider.filledTrackAnchorValue = model.anchorValue;
|
|
_slider.shouldDisplayDiscreteValueLabel = model.discreteValueLabel;
|
|
_slider.thumbHollowAtStart = model.hollowCircle;
|
|
_slider.enabled = model.enabled;
|
|
_slider.hapticsEnabled = model.hapticsEnabled;
|
|
_slider.shouldEnableHapticsForAllDiscreteValues = model.shouldEnableHapticsForAllDiscreteValues;
|
|
|
|
// Don't apply a `nil` color, use the default
|
|
if (model.sliderColor) {
|
|
[_slider setTrackFillColor:model.sliderColor forState:UIControlStateNormal];
|
|
[_slider setThumbColor:model.sliderColor forState:UIControlStateNormal];
|
|
_slider.inkColor = model.sliderColor;
|
|
}
|
|
|
|
if (model.trackBackgroundColor) {
|
|
[_slider setTrackBackgroundColor:model.trackBackgroundColor forState:UIControlStateNormal];
|
|
}
|
|
|
|
if (model.filledTickColor) {
|
|
[_slider setFilledTrackTickColor:model.filledTickColor forState:UIControlStateNormal];
|
|
}
|
|
|
|
if (model.backgroundTickColor) {
|
|
[_slider setBackgroundTrackTickColor:model.backgroundTickColor forState:UIControlStateNormal];
|
|
}
|
|
|
|
// Add target/action pair
|
|
[_slider addTarget:model
|
|
action:@selector(didChangeMDCSliderValue:)
|
|
forControlEvents:UIControlEventValueChanged];
|
|
}
|
|
|
|
- (void)prepareForReuse {
|
|
[super prepareForReuse];
|
|
|
|
// Remove target/action pairs
|
|
NSSet *targets = [_slider allTargets];
|
|
for (id target in targets) {
|
|
[_slider removeTarget:target action:NULL forControlEvents:UIControlEventValueChanged];
|
|
}
|
|
}
|
|
|
|
- (void)layoutSubviews {
|
|
[super layoutSubviews];
|
|
|
|
UIEdgeInsets safeArea = UIEdgeInsetsZero;
|
|
// Accommodate insets for iPhone X.
|
|
safeArea = self.safeAreaInsets;
|
|
safeArea.top = 0;
|
|
CGRect labelFrame =
|
|
CGRectMake(kSliderHorizontalMargin + 6, kSliderVerticalMargin,
|
|
self.contentView.frame.size.width - (2 * kSliderHorizontalMargin), 20);
|
|
|
|
_label.frame = UIEdgeInsetsInsetRect(labelFrame, safeArea);
|
|
|
|
CGSize sliderSize = [_slider intrinsicContentSize];
|
|
sliderSize.width = MAX(kSliderMinimumTouchSize, sliderSize.width);
|
|
sliderSize.height = MAX(kSliderMinimumTouchSize, sliderSize.height);
|
|
CGRect sliderFrame = CGRectMake(
|
|
kSliderHorizontalMargin,
|
|
self.contentView.frame.size.height - kSliderVerticalMargin - sliderSize.height,
|
|
self.contentView.frame.size.width - (2 * kSliderHorizontalMargin), sliderSize.height);
|
|
_slider.frame = UIEdgeInsetsInsetRect(sliderFrame, safeArea);
|
|
}
|
|
|
|
- (void)setLabelFont:(UIFont *)labelFont {
|
|
_label.font = labelFont;
|
|
}
|
|
|
|
- (UIFont *)labelFont {
|
|
return _label.font;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface MDCSliderFlowLayout : UICollectionViewFlowLayout
|
|
@end
|
|
|
|
@implementation MDCSliderFlowLayout
|
|
|
|
- (BOOL)shouldInvalidateLayoutForBoundsChange:(CGRect)newBounds {
|
|
if (!CGSizeEqualToSize(self.collectionView.bounds.size, newBounds.size)) {
|
|
[self invalidateLayout];
|
|
return YES;
|
|
}
|
|
return NO;
|
|
}
|
|
|
|
- (void)invalidateLayout {
|
|
[super invalidateLayout];
|
|
|
|
[self.collectionView setNeedsLayout];
|
|
}
|
|
|
|
- (CGSize)itemSize {
|
|
return CGSizeMake(self.collectionView.bounds.size.width, 100);
|
|
}
|
|
|
|
- (CGFloat)minimumInteritemSpacing {
|
|
return 0;
|
|
}
|
|
|
|
@end
|
|
|
|
@interface SliderCollectionViewController : UICollectionViewController
|
|
@property(nonatomic, strong) MDCSemanticColorScheme *colorScheme;
|
|
@end
|
|
|
|
@implementation SliderCollectionViewController {
|
|
NSMutableArray<MDCSliderModel *> *_sliders;
|
|
MDCTypographyScheme *_typographyScheme;
|
|
}
|
|
|
|
- (instancetype)init {
|
|
MDCSliderFlowLayout *layout = [[MDCSliderFlowLayout alloc] init];
|
|
if (self = [super initWithCollectionViewLayout:layout]) {
|
|
// Register cell class.
|
|
[self.collectionView registerClass:[MDCSliderExampleCollectionViewCell class]
|
|
forCellWithReuseIdentifier:kReusableIdentifierItem];
|
|
|
|
self.collectionView.alwaysBounceVertical = YES;
|
|
self.collectionView.backgroundColor = [UIColor whiteColor];
|
|
|
|
_typographyScheme = [[MDCTypographyScheme alloc] init];
|
|
|
|
// Init the sliders
|
|
_sliders = [[NSMutableArray alloc] init];
|
|
MDCSliderModel *model;
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Default slider";
|
|
model.value = (CGFloat)0.66;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Green slider without hollow circle at 0";
|
|
model.sliderColor = MDCPalette.greenPalette.tint800;
|
|
model.hollowCircle = NO;
|
|
model.value = 0;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Discrete slider with numeric value label";
|
|
model.numDiscreteValues = 5;
|
|
model.value = (CGFloat)0.2;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Discrete slider without numeric value label";
|
|
model.numDiscreteValues = 7;
|
|
model.value = 1;
|
|
model.discreteValueLabel = NO;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Discrete slider with full haptics";
|
|
model.numDiscreteValues = 5;
|
|
model.value = 1;
|
|
model.discreteValueLabel = NO;
|
|
model.shouldEnableHapticsForAllDiscreteValues = YES;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Dark themed slider";
|
|
model.labelColor = [UIColor whiteColor];
|
|
model.trackBackgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:(CGFloat)0.3];
|
|
model.sliderColor = MDCPalette.bluePalette.tint500;
|
|
model.bgColor = [UIColor darkGrayColor];
|
|
model.value = (CGFloat)0.2;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Anchored slider";
|
|
model.anchorValue = (CGFloat)0.5;
|
|
model.value = (CGFloat)0.7;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Haptics Disabled Slider";
|
|
model.value = (CGFloat)0.66;
|
|
model.hapticsEnabled = NO;
|
|
[_sliders addObject:model];
|
|
|
|
model = [[MDCSliderModel alloc] init];
|
|
model.labelString = @"Disabled slider";
|
|
model.value = (CGFloat)0.5;
|
|
model.anchorValue = (CGFloat)0.1;
|
|
model.enabled = NO;
|
|
[_sliders addObject:model];
|
|
|
|
_colorScheme =
|
|
[[MDCSemanticColorScheme alloc] initWithDefaults:MDCColorSchemeDefaultsMaterial201804];
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
#pragma mark - <UICollectionViewDataSource>
|
|
|
|
- (NSInteger)collectionView:(UICollectionView *)collectionView
|
|
numberOfItemsInSection:(NSInteger)section {
|
|
return [_sliders count];
|
|
}
|
|
|
|
- (UICollectionViewCell *)collectionView:(UICollectionView *)collectionView
|
|
cellForItemAtIndexPath:(NSIndexPath *)indexPath {
|
|
MDCSliderExampleCollectionViewCell *cell =
|
|
[collectionView dequeueReusableCellWithReuseIdentifier:kReusableIdentifierItem
|
|
forIndexPath:indexPath];
|
|
MDCSliderModel *model = [_sliders objectAtIndex:indexPath.item];
|
|
[cell applyModel:model withColorScheme:self.colorScheme];
|
|
cell.labelFont = _typographyScheme.subtitle2;
|
|
return cell;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation SliderCollectionViewController (CatalogByConvention)
|
|
|
|
+ (NSDictionary *)catalogMetadata {
|
|
return @{
|
|
@"breadcrumbs" : @[ @"Slider", @"Slider" ],
|
|
@"description" : @"Sliders allow users to make selections from a range of values.",
|
|
@"primaryDemo" : @YES,
|
|
@"presentable" : @YES,
|
|
};
|
|
}
|
|
|
|
@end
|