工作中总结的自定义控件(一)
来源:互联网 发布:复杂网络的特性 编辑:程序博客网 时间:2024/06/06 04:21
今天总结了一下平时工作中为那些奇葩的UI设计自定义的控件,下面一个个分享给大家。
一、第一个是tableView的透明度渐变效果
1、效果:
很多app用到了这种效果,比如歌词显示、直播间聊天记录等。
大致效果如下:
2、使用方法:
/* * frame:tableView的frame * direction:透明渐进的方向 * gradualValue:透明范围值,如果只有一个方向,此值传一个NSNumber、NSString即可,值的范围0—1。如果是两个方向,则需要传一个数组,数组里边传两个NSNumber或者NSString ***/+ (instancetype)gradualTableViewWithFrame:(CGRect)frame direction:(WZBTableViewGradualDirection)direction gradualValue:(id)gradualValue;
参数值说明一下,direction代表方向,是一个位移枚举,如果想让tableView顶部渐变,则此值为WZBTableViewGradualDirectionTop,如果为底部渐变,则此值为WZBTableViewGradualDirectionBottom,如果上下都要渐变,则需要WZBTableViewGradualDirectionTop | WZBTableViewGradualDirectionBottom。gradualValue代表渐变范围值,值的范围为0-1,如果想让顶部20%渐变,此值为@(0.2)。如果想顶部底部都有20%渐变,此值为@[@(0.2), @(0.2)]。
如下:
WZBGradualTableView *tableView = [WZBGradualTableView gradualTableViewWithFrame:self.view.bounds direction:(WZBTableViewGradualDirectionTop | WZBTableViewGradualDirectionBottom) gradualValue:@[@(.3), @0.3]];
则显示效果为:
如果这样写
[WZBGradualTableView gradualTableViewWithFrame:CGRectMake(0, self.view.frame.size.height - 180, self.view.frame.size.width, 140) direction:WZBTableViewGradualDirectionTop gradualValue:@.3f]
效果如下:
3、实现大致原理:
这种渐变效果主要用到tableView的mask属性,我们首先要创建一个CAGradientLayer,此类的使用网上介绍有很多,在这里不再赘述,不懂得私聊我,或者加入我的技术群。
上代码:
if (!self.layer.mask) { CAGradientLayer *maskLayer = [CAGradientLayer layer]; maskLayer.locations = @[@0.0, topValue, @(1-bottomValue.doubleValue), @1.0f]; maskLayer.bounds = CGRectMake(0, 0, self.frame.size.width, self.frame.size.height); maskLayer.anchorPoint = CGPointZero; self.layer.mask = maskLayer; }[self addObserver:self forKeyPath:@"contentOffset" options:NSKeyValueObservingOptionNew context:nil];
由于tableView可以滑动,滑动的时候就需要实时的作出改变,因此我这里使用KVO监听“contentOffset”属性,每当contentOffset发生改变,证明用户滑动了tableView,这时候需要调用的代码为:
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context { if ([keyPath isEqualToString:@"contentOffset"]) { [self change]; }}- (void)change { UIScrollView *scrollView = (UIScrollView *)self; CGColorRef outerColor = [UIColor colorWithWhite:1.0 alpha:0.0].CGColor; CGColorRef innerColor = [UIColor colorWithWhite:1.0 alpha:1.0].CGColor; NSArray *colors; if (scrollView.contentOffset.y + scrollView.contentInset.top <= 0) { //Top of scrollView colors = @[(__bridge id) innerColor, (__bridge id) innerColor, (__bridge id) innerColor, (__bridge id) outerColor]; } else if (scrollView.contentOffset.y + scrollView.frame.size.height >= scrollView.contentSize.height) { //Bottom of tableView colors = @[(__bridge id) outerColor, (__bridge id) innerColor, (__bridge id) innerColor, (__bridge id) innerColor]; } else { //Middle colors = @[(__bridge id) outerColor, (__bridge id) innerColor, (__bridge id) innerColor, (__bridge id) outerColor]; } ((CAGradientLayer *) scrollView.layer.mask).colors = colors; [CATransaction begin]; [CATransaction setDisableActions:YES]; scrollView.layer.mask.position = CGPointMake(0, scrollView.contentOffset.y); [CATransaction commit];}
别忘了移除观察者:
- (void)dealloc { [self removeObserver:self forKeyPath:@"contentOffset"];}
4、GitHub源码地址:WZBGradualTableView
二、自定义的开关控件
1、效果:
2、使用方法:
将WZBSwitch.h和WZBSwitch.m拖入工程
在需要使用的地方调用
/** 初始化方法 * switchValueChange: 开关状态改变回调block */ WZBSwitch *switchView = [[WZBSwitch alloc] initWithFrame:CGRectMake(100, 100, 50, 25) switchValueChanged:^(WZBSwitch *swith, BOOL on) { // do someing }]; [self.view addSubview:switchView];
对于开关状态的监听或者您还可以通过代理:
WZBSwitch *switchView = [[WZBSwitch alloc] initWithFrame:CGRectMake(100, 100, 50, 25)]; [self.view addSubview:switchView]; // delegate switchView.delegate = self;
然后实现代理方法即可
#pragma mark - WZBSwitchDelegate - (void)switchValueChange:(WZBSwitch *)swith on:(BOOL)on { // do someing }
如果您想自定义开关颜色,代码如下:
//设置所有颜色 [switchView setUpAllColors:^NSDictionary *(UIColor *__autoreleasing *onTintColor, UIColor *__autoreleasing *onBackgroundColor, UIColor *__autoreleasing *offTintColor, UIColor *__autoreleasing *offBackgroundColor, UIColor *__autoreleasing *tintColor) { // 可以通过这种方法设置需要设置的颜色 *onTintColor = [UIColor redColor]; *onBackgroundColor = [UIColor blueColor]; *offTintColor = [UIColor greenColor]; *offBackgroundColor = [UIColor grayColor]; *tintColor = [UIColor blackColor]; return nil; }];
或者这样
[switchView setUpAllColors:^NSDictionary *(UIColor *__autoreleasing *onTintColor, UIColor *__autoreleasing *onBackgroundColor, UIColor *__autoreleasing *offTintColor, UIColor *__autoreleasing *offBackgroundColor, UIColor *__autoreleasing *tintColor) { // 也可以通过这种方法设置需要设置的颜色 return @{OnTintColor : WZBColor(234, 67, 53), OnBackgroundColor : WZBColor(244, 161, 154), OffTintColor : WZBColor(255, 255, 255), OffBackgroundColor : WZBColor(214, 214, 214), TintColor : [UIColor colorWithRed:0.8252 green:0.8252 blue:0.8252 alpha:1.0]}; }];
3、实现大致原理:
此控件由两部分组成,顶部View和底部View
@property (nonatomic, strong) UIView *topView;@property (nonatomic, strong) UIView *bottomView;
/** 一个方法设置所有颜色 && block回调 * switchValueChange: 开关状态改变回调block */- (void)setUpAllColors:(NSDictionary *(^)(UIColor **onTintColor,UIColor **onBackgroundColor, UIColor **offTintColor, UIColor **offBackgroundColor, UIColor **tintColor))allColorBlock switchValueChanged:(SwitchValueChangeBlock)switchValueChange;;
这个方法有两个block参数,第一个可以设置您所需要设置的所有颜色值,第二个block是当开关状态发生改变的时候回调
/** 设置开关状态, animated : 是否有动画 */- (void)setOn:(BOOL)newOn animated:(BOOL)animated;
此方法用于设置开关状态
@protocol WZBSwitchDelegate <NSObject>@optional- (void)switchValueChange:(WZBSwitch *)swith on:(BOOL)on;@end
如果您不喜欢使用block,我还提供了代理方法监听开关状态的改变
- (void)setOn:(BOOL)newOn animated:(BOOL)animated {// if (_on == newOn) return; __block CGRect frame = self.topView.frame; CGFloat newX = newOn ? self.frame.size.width - self.topView.frame.size.width : 0; [UIView animateWithDuration:animated ? 0.2 : 0.0 animations:^{ frame.origin.x = newX; self.topView.frame = frame; [self setSwitchColorWithStatus:newOn]; } completion:^(BOOL finished) { if (finished) { // delegate if ([self.delegate respondsToSelector:@selector(switchValueChange:on:)]) { [self.delegate switchValueChange:self on:newOn]; } // block if (self.switchValueChange) { self.switchValueChange(self, newOn); } } }]; _on = newOn;}
当外界调用方法改变开关状态时,动画/非动画,改变上层View的frame即可
4、GitHub源码地址:WZBSwitch
三、一个仿网易的Segment
1、效果:
2、使用方法:
将WZBSegmentedControl.h和WZBSegmentedControl.m拖入工程
在需要使用的地方调用
/** 初始化方法 * titles: 所有标题 * titleClick: 点击标题的block回调 */ WZBSegmentedControl *segmentedControl = [WZBSegmentedControl segmentWithFrame:(CGRect){0, 0, 170, 25} titles:[self titles] titleClick:^(NSInteger index) { // do soming }]; self.navigationItem.titleView = segmentedControl;
3、实现大致原理:
此控件还不是很完善,目前只提供一个初始化方法:
/* 初始化方法 * frame:控件frame * titleClick:点击title的时候block回调 **/+ (instancetype)segmentWithFrame:(CGRect)frame titles:(NSArray *)titles titleClick:(void(^)(NSInteger index))titleClick;
- (void)setContentOffset:(CGPoint)contentOffset { CGRect frame = self.backgroundView.frame; frame.origin.x = contentOffset.x; self.backgroundView.frame = frame; // 找出要操作的两个button设置颜色(目前先这样写,后续改进) for (UIView *v in self.subviews) { if ([v isKindOfClass:[UIButton class]]) { UIButton *button = (UIButton *)v; CGFloat overLapWidth = CGRectIntersection(button.frame, self.backgroundView.frame).size.width; NSInteger gb = 255 - overLapWidth * (255 / (self.frame.size.width / self.titles.count)); [button setTitleColor:WZBColor(255, gb, gb) forState:UIControlStateNormal]; } }}
核心方法:改变底部白色滑块的位置,但是如果注意观察,有个注意点是,在滑动的时候title的文字也会随着渐变,目前先试用这个方法临时解决,后续会提供方法设置文字颜色、选中文字颜色、背景颜色以及滑块颜色等。
4、GitHub源码地址:WZBSegmentedControl
———————————-难道我是分割线—————————–
11月13日更新:
本篇文章最后一个内容由于当时太匆忙写的不完整,今天补充一下。
增加了几个方法设置内部控件颜色
/* 设置文字颜色 * normalColor:未选中的按钮文字颜色 * selectColor:选中的按钮文字颜色 */- (void)setNormalColor:(UIColor *)normalColor selectColor:(UIColor *)selectColor;/* 设置部分颜色 * normalColor:未选中的按钮文字颜色 * selectColor:选中的按钮文字颜色 * edgingColor:边框颜色 */- (void)setNormalColor:(UIColor *)normalColor selectColor:(UIColor *)selectColor edgingColor:(UIColor *)edgingColor;/* 设置所有颜色 * normalColor:未选中的按钮文字颜色 * selectColor:选中的按钮文字颜色 * sliderColor:滑块背景颜色 * edgingColor:边框颜色 */- (void)setNormalColor:(UIColor *)normalColor selectColor:(UIColor *)selectColor sliderColor:(UIColor *)sliderColor edgingColor:(UIColor *)edgingColor;/* 设置所有属性 * normalColor:未选中的按钮文字颜色 * selectColor:选中的按钮文字颜色 * sliderColor:滑块背景颜色 * edgingColor:边框颜色 * edgingWidth:边框宽度 */- (void)setNormalColor:(UIColor *)normalColor selectColor:(UIColor *)selectColor sliderColor:(UIColor *)sliderColor edgingColor:(UIColor *)edgingColor edgingWidth:(CGFloat)edgingWidth;
有了这些方法你就可以愉快的用各种各样的颜色了
除此之外还在.h放出了这些属性
// 所有title@property (nonatomic, strong, readonly) NSArray *titles;// 底部的滑块@property (nonatomic, strong, readonly) UIView *backgroundView;// 辅助属性,当前选中的Button@property (nonatomic, strong, readonly) UIButton *selectButton;// 为选中的button颜色@property (nonatomic, strong) UIColor *normalColor;// 选中的button颜色@property (nonatomic, strong) UIColor *selectColor;// 滑块颜色@property (nonatomic, strong) UIColor *sliderColor;// 边框颜色@property (nonatomic, strong) UIColor *edgingColor;// 边框颜色@property (nonatomic, assign) CGFloat edgingWidth;
以便使用者可以单独设置某个颜色
// 点击title的block回调@property (nonatomic, copy) void(^tClick)(NSInteger index);// 点击title的block回调,selectButton:选中的button@property (nonatomic, copy) void(^titleClick)(NSInteger index, UIButton *selectButton);
还有这两个block,一个block只有选中下标参数,另外一个有选中下标和选中的button两个参数
好吧!我承认很多人喜欢用代理而不是block,为什么不提供代理方法呢?OK,Here!
@protocol WZBSegmentedControlDelegate <NSObject>@optional// segmented点击的时候调用,selectIndex:选中的index- (void)segmentedValueDidChange:(WZBSegmentedControl *)segment selectIndex:(NSInteger)selectIndex;// segmented点击的时候调用,selectIndex:选中的index,fromeIndex:从哪个index点过来的- (void)segmentedValueDidChange:(WZBSegmentedControl *)segment selectIndex:(NSInteger)selectIndex fromeIndex:(NSInteger)fromeIndex;// segmented点击的时候调用,selectIndex:选中的index,fromeIndex:从哪个index点过来的,selectButton:选中的button- (void)segmentedValueDidChange:(WZBSegmentedControl *)segment selectIndex:(NSInteger)selectIndex fromeIndex:(NSInteger)fromeIndex selectButton:(UIButton *)selectButton;// segmented点击的时候调用,selectIndex:选中的index,fromeIndex:从哪个index点过来的,selectButton:选中的button,allButtons:所有的button- (void)segmentedValueDidChange:(WZBSegmentedControl *)segment selectIndex:(NSInteger)selectIndex fromeIndex:(NSInteger)fromeIndex selectButton:(UIButton *)selectButton allButtons:(NSArray *)allButtons;@end
注释很清楚
简单给大家讲下拖动的时候颜色渐变的实现,直接上代码
// 根据颜色拿到RGB数值void getRGBValue(CGFloat colorArr[3], UIColor *color) { unsigned char data[4]; // 宽,高,内存中像素的每个组件的位数(RGB应该为32),bitmap的每一行在内存所占的比特数 size_t width = 1, height = 1, bitsPerComponent = 8, bytesPerRow = 4; // bitmap上下文使用的颜色空间 CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB(); // 指定bitmap是否包含alpha通道 uint32_t bitmapInfo = 1; // 创建一个位图上下文。当你向上下文中绘制信息时,Quartz把你要绘制的信息作为位图数据绘制到指定的内存块。一个新的位图上下文的像素格式由三个参数决定:每个组件的位数,颜色空间,alpha选项。alpha值决定了绘制像素的透明性 CGContextRef context = CGBitmapContextCreate(&data, width, height, bitsPerComponent, bytesPerRow, space, bitmapInfo); // 设置当前上下文中填充颜色 CGContextSetFillColorWithColor(context, [color CGColor]); // 在此区域内填入当前填充颜色 CGContextFillRect(context, CGRectMake(0, 0, 1, 1)); CGContextRelease(context); CGColorSpaceRelease(space); for (NSInteger i = 0; i < 3; i++) { colorArr[i] = data[i]; }}
这是写的一个c语言函数,每句基本都有注释,说白了就是把RGB颜色拆分开,R是多少,G是多少,B是多少,然后放数组里。
// 找出要操作的两个button设置颜色 NSMutableArray *buttonArr = [NSMutableArray array]; for (UIButton *button in self.allButtons) { CGFloat overLapWidth = CGRectIntersection(button.frame, self.backgroundView.frame).size.width; if (overLapWidth > 0) { [buttonArr addObject:button]; } } // 切换的时候 if (buttonArr.count > 1) { UIButton *leftButton = buttonArr.firstObject; UIButton *rightButton = buttonArr.lastObject; // 设置要渐变的两个button颜色 [rightButton setTitleColor:WZBColor([self getRGBValueWithIndex:0 button:rightButton], [self getRGBValueWithIndex:1 button:rightButton], [self getRGBValueWithIndex:2 button:rightButton]) forState:UIControlStateNormal]; [leftButton setTitleColor:WZBColor([self getRGBValueWithIndex:0 button:leftButton], [self getRGBValueWithIndex:1 button:leftButton], [self getRGBValueWithIndex:2 button:leftButton]) forState:UIControlStateNormal]; }
先找到两个需要更改颜色的button,按照button和底部滑块交叉区域的宽度比例,切换每个R、G、B的值,具体大家可以下载最新的源码
- 工作中总结的自定义控件(一)
- Android自定义控件总结(一)
- 我工作中所遇到的业务总结(一)
- Android 中自定义控件总结
- 干货!老司机工作中用到的自定义控件,总有一个适合你的(一)
- 干货!老司机工作中用到的自定义控件,总有一个适合你的(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- 自定义控件(一)
- python os.path模块常用方法详解
- KNN(K-最近邻分类)分类算法
- IDEA 快速创建spring boot入门小Demo
- RSA 私钥和公钥的一些自己的理解
- 常用的正则表达式去重、匹配img标签、13位时间戳
- 工作中总结的自定义控件(一)
- 正则表达式
- C++ fstream文件操作
- 自学nginx(四): 禁止从某国家的访问
- PHP Mongodb 连接远程数据库
- Slim框架引入Eloquent ORM
- 蒋勇 | 白话区块链技术栈与应用
- android 初识APK瘦身
- QDialog中的QButtonBox的信号与槽函数在不同对象先后顺序问题