iOS-關於自定義分段選擇器的一些小事(Segmented)

来源:https://www.cnblogs.com/wangkejia/archive/2019/07/09/11158051.html
-Advertisement-
Play Games

系統自帶的分段選擇就是 UISegmentedControl ,也有一些大佬自定義的 Segmented ,比如Git上的 HMSegmentedControl ,我以前最初的項目中,也有用到過,如果自己寫,或者想自定義一些UI,該從哪裡出發,其實在用過 HMSegmentedControl 之後, ...


系統自帶的分段選擇就是 UISegmentedControl ,也有一些大佬自定義的 Segmented ,比如Git上的 HMSegmentedControl ,我以前最初的項目中,也有用到過,如果自己寫,或者想自定義一些UI,該從哪裡出發,其實在用過 HMSegmentedControl 之後,大致就有思路了,如果想簡單的實現下,可以利用 UICollectionView 來實現,下麵是我利用 UICollectionView 寫的一個簡單的小慄子,效果圖

 

設計思路

 

首先利用 UICollectionView 處理每個item的大小,確切的說是寬度,那麼就要每次選中一個item後,重新計算所有的item的寬度,同時計算 UICollectionView 的 contentSize.width;

計算每個item寬度分為兩種情況,一種是選中的字體的顯示,一種是未選中的字體顯示,比如字體大小,顏色等,然後根據字體大小字元串長度,計算出字體需要展示的寬度,並計算對應的item寬度,最後把每個item的寬度保存起來,用於在 UICollectionView 代理方法中做處理;

計算 contentSize.width 由上圖可知,是由兩邊的間距,item之間的間距和每個item的總和,目的是利用 contentSize.width 計算下劃線的位置;

具體實現

#import "XKCollectionView.h"

///四周邊距
const static CGFloat _margin_left = 5;
const static CGFloat _margin_right = 5;
const static CGFloat _margin_top = 0;
const static CGFloat _margin_bottom = 2;
const static CGFloat _margin_space = 15;

const static CGFloat _line_width = 30.0;
const static CGFloat _line_height = 3.0;

@interface XKCollectionView ()<UICollectionViewDataSource,UICollectionViewDelegateFlowLayout>
///臨時數據
@property (nonatomic,strong) NSArray<NSString *> *titleArray;
///每個item的寬度
@property (nonatomic,strong) NSMutableArray *widthsArray;
///底部線條
@property (nonatomic,strong) UIView *lineView;
///選中的item索引
@property (nonatomic,assign) NSInteger selectIndex;
///選中的item string
@property (nonatomic,strong) NSString *selectString;
////計算出來的總寬度,用於設置 UICollectionView.contentSize.width
@property (nonatomic,assign) CGFloat totalContentWidth;
@end
@implementation XKCollectionView
- (instancetype)initWithFrame:(CGRect)frame collectionViewLayout:(UICollectionViewLayout *)layout{
    self = [super initWithFrame:frame collectionViewLayout:layout];
    if (self) {
        [self setup];
    }
    return self;
}
- (void)setup{
    _selectIndex = 0;
    self.widthsArray = [NSMutableArray array];
    [self addSubview:self.lineView];
    self.backgroundColor = [UIColor whiteColor];
    self.showsHorizontalScrollIndicator = NO;
    self.delegate = self;
    self.dataSource = self;
    [self registerClass:[XKCollectionViewCell class] forCellWithReuseIdentifier:@"XKCollectionViewCell"];
    
    _titleArray = @[@"一級建造師",@"二級建造師",@"造價工程師",@"咨詢工程師",@"註冊安全工程師",@"監理工程師",@"註冊電氣工程師",@"環境影響評價工程師",@"註冊城鄉規劃師",@"註冊消防工程師"];
    [self storeSegmentedWidth];
    [self reloadData];
    CGRect lineRext = [self measureLineFrame];
    self.lineView.frame = lineRext;
    ///設置偏移量
    [self setContentOffset:CGPointMake([self measureContentOffsetX], 0)];
}


- (void)updateSelectSeg{
    [self storeSegmentedWidth];
    [self reloadData];
    [UIView animateWithDuration:0.3 animations:^{
        CGRect lineRext = [self measureLineFrame];
        self.lineView.frame = lineRext;
    }];
    
    [self setContentOffset:CGPointMake([self measureContentOffsetX], 0) animated:YES];
}
#pragma mark ========== 儲存計算好的item寬度 ==========
///每次切換時更新
- (void)storeSegmentedWidth{
    _selectIndex = 0;
    _totalContentWidth = 0;
    [self.widthsArray removeAllObjects];
    
    if (_selectString) {
        for (int i = 0; i < _titleArray.count; i ++) {
            NSString *title = _titleArray[i];
            if ([title isEqualToString:_selectString]) {
                _selectIndex = i;
                break;
            }
        }
    }
   
    
    for (int i = 0; i < _titleArray.count; i ++) {
        
        CGSize size = [self measureTitleIndex:i];
        NSNumber *value = [NSNumber numberWithFloat:size.width];
        [self.widthsArray addObject:value];
        
        _totalContentWidth = _totalContentWidth + size.width;
        if (i < _titleArray.count - 1) {
            _totalContentWidth = _totalContentWidth + _margin_space;
        }
    }
    _totalContentWidth = _totalContentWidth + _margin_left + _margin_right;
    
}
- (CGSize)measureTitleIndex:(NSUInteger)index {
    if (index >= _titleArray.count) {
        return CGSizeZero;
    }
    
    id title = _titleArray[index];
    CGSize size = CGSizeZero;
    BOOL selected = (index == _selectIndex);
    NSDictionary *titleAttrs = selected ? [self resultingSelectedTitleTextAttributes] : [self resultingTitleTextAttributes];
    size = [(NSString *)title sizeWithAttributes:titleAttrs];
    UIFont *font = titleAttrs[@"NSFont"];
    size = CGSizeMake(ceil(size.width), ceil(size.height - font.descender));
    CGSize resault = CGRectIntegral((CGRect){CGPointZero, size}).size;
    return resault;
}
- (NSDictionary *)resultingSelectedTitleTextAttributes {
    NSDictionary *resultingAttrs = @{NSForegroundColorAttributeName : [UIColor blackColor] ,NSFontAttributeName:[UIFont fontWithName:@"Helvetica-Bold" size:18.0]};
    return resultingAttrs;
}
- (NSDictionary *)resultingTitleTextAttributes {
    NSDictionary *resultingAttrs = @{NSForegroundColorAttributeName : [UIColor lightGrayColor],NSFontAttributeName:[UIFont systemFontOfSize:14.0]};
    return resultingAttrs;
}
#pragma mark ========== 計算下劃線位置 ==========
- (CGRect)measureLineFrame{
    CGRect lineRect = CGRectZero;
    CGFloat lineRectX = 0;
    for (int i = 0; i < _selectIndex; i ++) {
        NSNumber *number = self.widthsArray[i];
        lineRectX = lineRectX + [number floatValue] + _margin_space;
    }
    CGFloat widthSelect = [self.widthsArray[_selectIndex] floatValue];
    CGFloat lastLocation = widthSelect >= _line_width ? (widthSelect - _line_width)/2 : (_line_width - widthSelect)/2;
    lineRectX = lineRectX + _margin_left + lastLocation;
    
    lineRect = CGRectMake(lineRectX, self.bounds.size.height - _line_height - 2, _line_width, _line_height);
    return lineRect;
}
#pragma mark ========== 計算偏移量 ==========
- (CGFloat)measureContentOffsetX{
    CGFloat selfWidth = self.bounds.size.width;
    
    ///先計算點擊的item中心點
    CGFloat selectedCenterX = 0;
    for (int i = 0; i < _selectIndex; i ++) {
        NSNumber *number = self.widthsArray[i];
        selectedCenterX = selectedCenterX + [number floatValue] + _margin_space;
    }
    CGFloat widthSelect = [self.widthsArray[_selectIndex] floatValue];
    selectedCenterX = selectedCenterX + widthSelect/2;
    
    if (_totalContentWidth <= selfWidth) {///充滿內部不做偏移
        return 0;
    }
    
    if (selectedCenterX <= selfWidth/2) {
        return 0;
    }
    else if (selectedCenterX >= _totalContentWidth - selfWidth/2){
        return _totalContentWidth - selfWidth;
    }
    else{
        return selectedCenterX - selfWidth/2;
    }
}
#pragma mark ========== 代理 ==========
- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section{
    return _titleArray.count;
}

- (CGFloat)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout minimumInteritemSpacingForSectionAtIndex:(NSInteger)section{
    return _margin_space;
}
- (UIEdgeInsets)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout insetForSectionAtIndex:(NSInteger)section{
    return UIEdgeInsetsMake(_margin_top, _margin_left, _margin_bottom, _margin_right);
}
//item大小
- (CGSize)collectionView:(UICollectionView *)collectionView layout:(UICollectionViewLayout *)collectionViewLayout sizeForItemAtIndexPath:(NSIndexPath *)indexPath{
    NSNumber *number = self.widthsArray[indexPath.row];
    return CGSizeMake([number floatValue],30);
}
- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath{
    XKCollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:@"XKCollectionViewCell" forIndexPath:indexPath];
    NSString *title = _titleArray[indexPath.row];
    cell.title = title;
    if (indexPath.row == _selectIndex) {
        cell.isSelectd = YES;
    }
    else{
        cell.isSelectd = NO;
    }
    return cell;
}
- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath{
    _selectString = _titleArray[indexPath.row];
    [self updateSelectSeg];
}
#pragma mark ========== 變數 ==========

- (UIView *)lineView{
    if(!_lineView){
        _lineView = [[UIView alloc]init];
        _lineView.backgroundColor = [UIColor purpleColor];
        _lineView.layer.masksToBounds = YES;
        _lineView.layer.cornerRadius = _line_height/2;
    }
    return _lineView;
}

@end

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1、文檔 1)ElasticSearch是面向文檔的,文檔是所有可搜索數據的最小單位。例如: a)日誌文件中的日誌項; b)一張唱片的詳細信息; c)一篇文章中的具體內容; 2)在ElasticSearch中,文檔會被序列化成Json格式: a)Json對象是由欄位組成的; b)每個欄位都有對應的字 ...
  • [TOC] 一、資料庫的安裝和連接 1.1 PyMySQL的安裝 1.2 python連接資料庫 1.2.1 更多參數版 二、創建表操作 三、操作數據 3.1 插入操作 3.2 查詢操作 Python查詢Mysql使用 fetchone() 方法獲取單條數據,使用 :方法獲取多條數據。 : 該方法獲 ...
  • Redis主從複製機制詳解 Redis有兩種不同的持久化方式,Redis伺服器通過持久化,把Redis記憶體中持久化到硬碟當中,當Redis宕機時,我們重啟Redis伺服器時,可以由RDB文件或AOF文件恢復記憶體中的數據。 不過持久化後的數據仍然只在一臺機器上,因此當硬體發生故障時,比如主板或CPU壞 ...
  • 第0步:版本選擇 AOSP版本選擇很重要,如果選錯了,會造成編譯失敗等各種問題,編譯AOSP對Xcode的版本是有要求的; 比如:AOSP6.0 7.0,要求Xcode的版本是8.3,然而在MacOS 10.14上面是不支持Xcode8.3的這就很尷尬; 由於現在大家的Mac環境基本是更新到最新的1 ...
  • 在Dart裡面,變數的聲明使用var、Object或Dynamic關鍵字,如下所示: var name = ‘張三’; 在Dart語言里一切皆為對象,所以如果沒有將變數初始化,那麼它的預設值為null(包括數字),如果需要判斷變數是否為null,則需要進行如下判斷: String name;if(n... ...
  • iOS查看屏幕幀數工具--YYFPSLabel iOS 保持界面流暢的技巧 iOS 優化界面流暢度的探討 先研究一下 改天自己出一篇文章 ...
  • 原文:https://www.jianshu.com/p/b5a484cecd7c 本文主要說明2018年蘋果開發者賬號申請的流程,申請流程相較於2017年有一些改變,希望大家能夠通過本文少走一些彎路,能夠順利完成開發者賬號的申請。關於新流程中可能出現的一些問題以及部分流程的變更均在下文中運用灰色塊 ...
  • 一、數據綁定 二、列表渲染 <!--index.wxml--> 三、綁定一個點擊事件 四、事件冒泡 五、事件傳參 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...