干貨!老司機工作中用到的自定義控件,總有一個適合你的(一)

今天總結了一下平時工作中為那些奇葩的UI設計自定義的控件,下面一個個分享給大家。


一、第一個是tableView的透明度漸變效果

1、效果:

很多app用到了這種效果,比如歌詞顯示、直播間聊天記錄等。
大致效果如下:

WZBGradualTableView
WZBGradualTableView

背景圖片截取自新浪直播,侵立刪
背景圖片截取自新浪直播,侵立刪
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
WZBGradualTableView

如果這樣寫

[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、效果:
WZBSwitch
WZBSwitch
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、效果:
WZBSegmentedControl
WZBSegmentedControl
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;

有了這些方法你就可以愉快的用各種各樣的顏色了

技術交流群:413050745

除此之外還在.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的值,具體大家可以下載最新的源碼

有不懂或者任何疑問的地方都可以在下方評論,或者隨時聯系我,您還可以用QQ掃描底部的二維碼加入我們的技術交流群,我在那里等著你!

想看更多,請點擊:干貨!老司機工作中用到的自定義控件,總有一個適合你的(二)

怎么樣,這些您學會怎么用了嗎?

請不要吝惜,隨手點個喜歡或者關注一下吧!您的支持是我最大的動力??!
此系列文章持續更新,您可以關注我以便及時查看我的最新文章或者您還可以加入我們的群,大家庭期待您的加入!

我們的社區
我們的社區
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內容