Няма описание

SGPageTitleView.m 52KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090
  1. //
  2. // SGPageTitleView.m
  3. // SGPagingViewExample
  4. //
  5. // Created by kingsic on 17/4/10.
  6. // Copyright © 2017年 kingsic. All rights reserved.
  7. //
  8. #import "SGPageTitleView.h"
  9. #import "UIView+SGPagingView.h"
  10. #import "UIButton+SGPagingView.h"
  11. #import "SGPageTitleViewConfigure.h"
  12. #define SGPageTitleViewWidth self.frame.size.width
  13. #define SGPageTitleViewHeight self.frame.size.height
  14. #pragma mark - - - SGPageTitleButton
  15. @interface SGPageTitleButton : UIButton
  16. @end
  17. @implementation SGPageTitleButton
  18. - (void)setHighlighted:(BOOL)highlighted {
  19. }
  20. @end
  21. #pragma mark - - - SGPageTitleView
  22. @interface SGPageTitleView ()
  23. /// SGPageTitleViewDelegate
  24. @property (nonatomic, weak) id<SGPageTitleViewDelegate> delegatePageTitleView;
  25. /// SGPageTitleView 配置信息
  26. @property (nonatomic, strong) SGPageTitleViewConfigure *configure;
  27. /// scrollView
  28. @property (nonatomic, strong) UIScrollView *scrollView;
  29. /// 指示器
  30. @property (nonatomic, strong) UIView *indicatorView;
  31. /// 底部分割线
  32. @property (nonatomic, strong) UIView *bottomSeparator;
  33. /// 保存外界传递过来的标题数组
  34. @property (nonatomic, strong) NSArray *titleArr;
  35. /// 存储标题按钮的数组
  36. @property (nonatomic, strong) NSMutableArray *btnMArr;
  37. /// tempBtn
  38. @property (nonatomic, strong) UIButton *tempBtn;
  39. /// 记录所有按钮文字宽度
  40. @property (nonatomic, assign) CGFloat allBtnTextWidth;
  41. /// 记录所有子控件的宽度
  42. @property (nonatomic, assign) CGFloat allBtnWidth;
  43. /// 标记按钮下标
  44. @property (nonatomic, assign) NSInteger signBtnIndex;
  45. /// 标记按钮是否点击
  46. @property (nonatomic, assign) BOOL signBtnClick;
  47. /// 开始颜色, 取值范围 0~1
  48. @property (nonatomic, assign) CGFloat startR;
  49. @property (nonatomic, assign) CGFloat startG;
  50. @property (nonatomic, assign) CGFloat startB;
  51. /// 完成颜色, 取值范围 0~1
  52. @property (nonatomic, assign) CGFloat endR;
  53. @property (nonatomic, assign) CGFloat endG;
  54. @property (nonatomic, assign) CGFloat endB;
  55. @end
  56. @implementation SGPageTitleView
  57. - (instancetype)initWithFrame:(CGRect)frame delegate:(id<SGPageTitleViewDelegate>)delegate titleNames:(NSArray *)titleNames configure:(SGPageTitleViewConfigure *)configure {
  58. if (self = [super initWithFrame:frame]) {
  59. self.backgroundColor = [[UIColor whiteColor] colorWithAlphaComponent:0.77];
  60. if (delegate == nil) {
  61. @throw [NSException exceptionWithName:@"SGPagingView" reason:@"SGPageTitleView 初始化方法中的代理必须设置" userInfo:nil];
  62. }
  63. self.delegatePageTitleView = delegate;
  64. if (titleNames == nil) {
  65. @throw [NSException exceptionWithName:@"SGPagingView" reason:@"SGPageTitleView 初始化方法中的标题数组必须设置" userInfo:nil];
  66. }
  67. self.titleArr = titleNames;
  68. if (configure == nil) {
  69. @throw [NSException exceptionWithName:@"SGPagingView" reason:@"SGPageTitleView 初始化方法中的配置信息必须设置" userInfo:nil];
  70. }
  71. self.configure = configure;
  72. [self initialization];
  73. [self setupSubviews];
  74. }
  75. return self;
  76. }
  77. + (instancetype)pageTitleViewWithFrame:(CGRect)frame delegate:(id<SGPageTitleViewDelegate>)delegate titleNames:(NSArray *)titleNames configure:(SGPageTitleViewConfigure *)configure {
  78. return [[self alloc] initWithFrame:frame delegate:delegate titleNames:titleNames configure:configure];
  79. }
  80. - (void)initialization {
  81. _selectedIndex = 0;
  82. }
  83. - (void)setupSubviews {
  84. // 0、处理偏移量
  85. UIView *tempView = [[UIView alloc] initWithFrame:CGRectZero];
  86. [self addSubview:tempView];
  87. // 1、添加 UIScrollView
  88. [self addSubview:self.scrollView];
  89. // 2、添加标题按钮
  90. [self setupTitleButtons];
  91. // 3、添加底部分割线
  92. if (self.configure.showBottomSeparator) {
  93. [self addSubview:self.bottomSeparator];
  94. }
  95. // 4、添加指示器
  96. if (self.configure.showIndicator) {
  97. [self.scrollView insertSubview:self.indicatorView atIndex:0];
  98. }
  99. }
  100. #pragma mark - - - layoutSubviews
  101. - (void)layoutSubviews {
  102. [super layoutSubviews];
  103. // 选中按钮下标初始值
  104. [self P_btn_action:self.btnMArr[_selectedIndex]];
  105. }
  106. #pragma mark - - - 计算字符串尺寸
  107. - (CGSize)P_sizeWithString:(NSString *)string font:(UIFont *)font {
  108. NSDictionary *attrs = @{NSFontAttributeName : font};
  109. return [string boundingRectWithSize:CGSizeMake(0, 0) options:NSStringDrawingUsesLineFragmentOrigin attributes:attrs context:nil].size;
  110. }
  111. #pragma mark - - - 懒加载
  112. - (NSArray *)titleArr {
  113. if (!_titleArr) {
  114. _titleArr = [NSArray array];
  115. }
  116. return _titleArr;
  117. }
  118. - (NSMutableArray *)btnMArr {
  119. if (!_btnMArr) {
  120. _btnMArr = [[NSMutableArray alloc] init];
  121. }
  122. return _btnMArr;
  123. }
  124. - (UIScrollView *)scrollView {
  125. if (!_scrollView) {
  126. _scrollView = [[UIScrollView alloc] init];
  127. _scrollView.showsVerticalScrollIndicator = NO;
  128. _scrollView.showsHorizontalScrollIndicator = NO;
  129. _scrollView.alwaysBounceHorizontal = YES;
  130. _scrollView.frame = CGRectMake(0, 0, SGPageTitleViewWidth, SGPageTitleViewHeight);
  131. if (_configure.needBounces == NO) {
  132. _scrollView.bounces = NO;
  133. }
  134. }
  135. return _scrollView;
  136. }
  137. - (UIView *)indicatorView {
  138. if (!_indicatorView) {
  139. _indicatorView = [[UIView alloc] init];
  140. if (self.configure.indicatorStyle == SGIndicatorStyleCover) {
  141. CGSize tempSize = [self P_sizeWithString:[self.btnMArr[0] currentTitle] font:self.configure.titleFont];
  142. CGFloat tempIndicatorViewH = tempSize.height;
  143. if (self.configure.indicatorHeight > self.SG_height) {
  144. _indicatorView.SG_y = 0;
  145. _indicatorView.SG_height = self.SG_height;
  146. } else if (self.configure.indicatorHeight < tempIndicatorViewH) {
  147. _indicatorView.SG_y = 0.5 * (self.SG_height - tempIndicatorViewH);
  148. _indicatorView.SG_height = tempIndicatorViewH;
  149. } else {
  150. _indicatorView.SG_y = 0.5 * (self.SG_height - self.configure.indicatorHeight);
  151. _indicatorView.SG_height = self.configure.indicatorHeight;
  152. }
  153. // 边框宽度及边框颜色
  154. _indicatorView.layer.borderWidth = self.configure.indicatorBorderWidth;
  155. _indicatorView.layer.borderColor = self.configure.indicatorBorderColor.CGColor;
  156. } else {
  157. CGFloat indicatorViewH = self.configure.indicatorHeight;
  158. _indicatorView.SG_height = indicatorViewH;
  159. _indicatorView.SG_y = self.SG_height - indicatorViewH - self.configure.indicatorToBottomDistance;
  160. }
  161. // 圆角处理
  162. if (self.configure.indicatorCornerRadius > 0.5 * _indicatorView.SG_height) {
  163. _indicatorView.layer.cornerRadius = 0.5 * _indicatorView.SG_height;
  164. } else {
  165. _indicatorView.layer.cornerRadius = self.configure.indicatorCornerRadius;
  166. }
  167. _indicatorView.backgroundColor = self.configure.indicatorColor;
  168. }
  169. return _indicatorView;
  170. }
  171. - (UIView *)bottomSeparator {
  172. if (!_bottomSeparator) {
  173. _bottomSeparator = [[UIView alloc] init];
  174. CGFloat bottomSeparatorW = self.SG_width;
  175. CGFloat bottomSeparatorH = 0.5;
  176. CGFloat bottomSeparatorX = 0;
  177. CGFloat bottomSeparatorY = self.SG_height - bottomSeparatorH;
  178. _bottomSeparator.frame = CGRectMake(bottomSeparatorX, bottomSeparatorY, bottomSeparatorW, bottomSeparatorH);
  179. _bottomSeparator.backgroundColor = self.configure.bottomSeparatorColor;
  180. }
  181. return _bottomSeparator;
  182. }
  183. #pragma mark - - - 添加标题按钮
  184. - (void)setupTitleButtons {
  185. NSInteger titleCount = self.titleArr.count;
  186. // 计算所有按钮的文字宽度
  187. [self.titleArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  188. CGSize tempSize = [self P_sizeWithString:obj font:self.configure.titleFont];
  189. CGFloat tempWidth = tempSize.width;
  190. self.allBtnTextWidth += tempWidth;
  191. }];
  192. // 所有按钮文字宽度 + 所有按钮额外增加的宽度
  193. self.allBtnWidth = self.allBtnTextWidth + self.configure.titleAdditionalWidth * titleCount;
  194. self.allBtnWidth = ceilf(self.allBtnWidth);
  195. if (self.allBtnWidth <= self.bounds.size.width) { // SGPageTitleView 静止样式
  196. CGFloat btnY = 0;
  197. CGFloat btnW = SGPageTitleViewWidth / titleCount;
  198. CGFloat btnH = 0;
  199. if (self.configure.indicatorStyle == SGIndicatorStyleDefault) {
  200. btnH = SGPageTitleViewHeight - self.configure.indicatorHeight;
  201. } else {
  202. btnH = SGPageTitleViewHeight;
  203. }
  204. CGFloat VSeparatorW = 1;
  205. CGFloat VSeparatorH = SGPageTitleViewHeight - self.configure.verticalSeparatorReduceHeight;
  206. if (VSeparatorH <= 0) {
  207. VSeparatorH = SGPageTitleViewHeight;
  208. }
  209. CGFloat VSeparatorY = 0.5 * (SGPageTitleViewHeight - VSeparatorH);
  210. for (NSInteger index = 0; index < titleCount; index++) {
  211. // 1、添加按钮
  212. SGPageTitleButton *btn = [[SGPageTitleButton alloc] init];
  213. CGFloat btnX = btnW * index;
  214. btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
  215. btn.tag = index;
  216. btn.titleLabel.font = self.configure.titleFont;
  217. [btn setTitle:self.titleArr[index] forState:(UIControlStateNormal)];
  218. [btn setTitleColor:self.configure.titleColor forState:(UIControlStateNormal)];
  219. [btn setTitleColor:self.configure.titleSelectedColor forState:(UIControlStateSelected)];
  220. [btn addTarget:self action:@selector(P_btn_action:) forControlEvents:(UIControlEventTouchUpInside)];
  221. [self.btnMArr addObject:btn];
  222. [self.scrollView addSubview:btn];
  223. // 2、添加按钮之间的分割线
  224. if (self.configure.showVerticalSeparator) {
  225. UIView *VSeparator = [[UIView alloc] init];
  226. if (index != 0) {
  227. CGFloat VSeparatorX = btnW * index - 0.5;
  228. VSeparator.frame = CGRectMake(VSeparatorX, VSeparatorY, VSeparatorW, VSeparatorH);
  229. VSeparator.backgroundColor = self.configure.verticalSeparatorColor;
  230. [self.scrollView addSubview:VSeparator];
  231. }
  232. }
  233. }
  234. self.scrollView.contentSize = CGSizeMake(SGPageTitleViewWidth, SGPageTitleViewHeight);
  235. } else { // SGPageTitleView 滚动样式
  236. CGFloat btnX = 0;
  237. CGFloat btnY = 0;
  238. CGFloat btnH = 0;
  239. if (self.configure.indicatorStyle == SGIndicatorStyleDefault) {
  240. btnH = SGPageTitleViewHeight - self.configure.indicatorHeight;
  241. } else {
  242. btnH = SGPageTitleViewHeight;
  243. }
  244. CGFloat VSeparatorW = 1;
  245. CGFloat VSeparatorH = SGPageTitleViewHeight - self.configure.verticalSeparatorReduceHeight;
  246. if (VSeparatorH <= 0) {
  247. VSeparatorH = SGPageTitleViewHeight;
  248. }
  249. CGFloat VSeparatorY = 0.5 * (SGPageTitleViewHeight - VSeparatorH);
  250. for (NSInteger index = 0; index < titleCount; index++) {
  251. // 1、添加按钮
  252. SGPageTitleButton *btn = [[SGPageTitleButton alloc] init];
  253. CGSize tempSize = [self P_sizeWithString:self.titleArr[index] font:self.configure.titleFont];
  254. CGFloat btnW = tempSize.width + self.configure.titleAdditionalWidth;
  255. btn.frame = CGRectMake(btnX, btnY, btnW, btnH);
  256. btnX = btnX + btnW;
  257. btn.tag = index;
  258. btn.titleLabel.font = self.configure.titleFont;
  259. [btn setTitle:self.titleArr[index] forState:(UIControlStateNormal)];
  260. [btn setTitleColor:self.configure.titleColor forState:(UIControlStateNormal)];
  261. [btn setTitleColor:self.configure.titleSelectedColor forState:(UIControlStateSelected)];
  262. [btn addTarget:self action:@selector(P_btn_action:) forControlEvents:(UIControlEventTouchUpInside)];
  263. [self.btnMArr addObject:btn];
  264. [self.scrollView addSubview:btn];
  265. // 2、添加按钮之间的分割线
  266. if (self.configure.showVerticalSeparator) {
  267. UIView *VSeparator = [[UIView alloc] init];
  268. if (index < titleCount - 1) {
  269. CGFloat VSeparatorX = btnX - 0.5;
  270. VSeparator.frame = CGRectMake(VSeparatorX, VSeparatorY, VSeparatorW, VSeparatorH);
  271. VSeparator.backgroundColor = self.configure.verticalSeparatorColor;
  272. [self.scrollView addSubview:VSeparator];
  273. }
  274. }
  275. }
  276. CGFloat scrollViewWidth = CGRectGetMaxX(self.scrollView.subviews.lastObject.frame);
  277. self.scrollView.contentSize = CGSizeMake(scrollViewWidth, SGPageTitleViewHeight);
  278. }
  279. // 标题文字渐变效果下对标题文字默认、选中状态下颜色的记录
  280. if (self.configure.titleGradientEffect) {
  281. [self setupStartColor:self.configure.titleColor];
  282. [self setupEndColor:self.configure.titleSelectedColor];
  283. }
  284. }
  285. #pragma mark - - - 标题按钮的点击事件
  286. - (void)P_btn_action:(UIButton *)button {
  287. // 1、改变按钮的选择状态
  288. [self P_changeSelectedButton:button];
  289. // 2、标题滚动样式下选中标题居中处理
  290. if (self.allBtnWidth > SGPageTitleViewWidth) {
  291. _signBtnClick = YES;
  292. [self P_selectedBtnCenter:button];
  293. }
  294. // 3、改变有关指示器的相关操作
  295. [self P_changeIndicatorWithButton:button];
  296. // 4、pageTitleViewDelegate
  297. if ([self.delegatePageTitleView respondsToSelector:@selector(pageTitleView:selectedIndex:)]) {
  298. [self.delegatePageTitleView pageTitleView:self selectedIndex:button.tag];
  299. }
  300. // 5、标记按钮下标
  301. _signBtnIndex = button.tag;
  302. }
  303. #pragma mark - - - 改变按钮的选择状态
  304. - (void)P_changeSelectedButton:(UIButton *)button {
  305. if (self.tempBtn == nil) {
  306. button.selected = YES;
  307. self.tempBtn = button;
  308. } else if (self.tempBtn != nil && self.tempBtn == button){
  309. button.selected = YES;
  310. } else if (self.tempBtn != button && self.tempBtn != nil){
  311. self.tempBtn.selected = NO;
  312. button.selected = YES;
  313. self.tempBtn = button;
  314. }
  315. UIFont *configureTitleSelectedFont = self.configure.titleSelectedFont;
  316. UIFont *defaultTitleFont = [UIFont systemFontOfSize:15];
  317. if ([configureTitleSelectedFont.fontName isEqualToString:defaultTitleFont.fontName] && configureTitleSelectedFont.pointSize == defaultTitleFont.pointSize) {
  318. // 标题文字缩放属性(开启 titleSelectedFont 属性将不起作用)
  319. if (self.configure.titleTextZoom == YES) {
  320. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  321. UIButton *btn = obj;
  322. btn.transform = CGAffineTransformIdentity;
  323. }];
  324. CGFloat afterZoomRatio = 1 + self.configure.titleTextZoomRatio;
  325. button.transform = CGAffineTransformMakeScale(afterZoomRatio, afterZoomRatio);
  326. }
  327. // 此处作用:避免滚动过程中点击标题手指不离开屏幕的前提下再次滚动造成的误差(由于文字渐变效果导致未选中标题的不准确处理)
  328. if (self.configure.titleGradientEffect == YES) {
  329. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  330. UIButton *btn = obj;
  331. btn.titleLabel.textColor = self.configure.titleColor;
  332. }];
  333. button.titleLabel.textColor = self.configure.titleSelectedColor;
  334. }
  335. } else {
  336. // 此处作用:避免滚动过程中点击标题手指不离开屏幕的前提下再次滚动造成的误差(由于文字渐变效果导致未选中标题的不准确处理)
  337. if (self.configure.titleGradientEffect == YES) {
  338. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  339. UIButton *btn = obj;
  340. btn.titleLabel.textColor = self.configure.titleColor;
  341. btn.titleLabel.font = self.configure.titleFont;
  342. }];
  343. button.titleLabel.textColor = self.configure.titleSelectedColor;
  344. button.titleLabel.font = self.configure.titleSelectedFont;
  345. } else {
  346. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  347. UIButton *btn = obj;
  348. btn.titleLabel.font = self.configure.titleFont;
  349. }];
  350. button.titleLabel.font = self.configure.titleSelectedFont;
  351. }
  352. }
  353. }
  354. #pragma mark - - - 标题滚动样式下选中标题居中处理
  355. - (void)P_selectedBtnCenter:(UIButton *)centerBtn {
  356. // 计算偏移量
  357. CGFloat offsetX = centerBtn.center.x - SGPageTitleViewWidth * 0.5;
  358. if (offsetX < 0) offsetX = 0;
  359. // 获取最大滚动范围
  360. CGFloat maxOffsetX = self.scrollView.contentSize.width - SGPageTitleViewWidth;
  361. if (offsetX > maxOffsetX) offsetX = maxOffsetX;
  362. // 滚动标题滚动条
  363. [self.scrollView setContentOffset:CGPointMake(offsetX, 0) animated:YES];
  364. }
  365. #pragma mark - - - 改变有关指示器的相关操作
  366. - (void)P_changeIndicatorWithButton:(UIButton *)button {
  367. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  368. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  369. self.indicatorView.SG_width = self.configure.indicatorFixedWidth;
  370. self.indicatorView.SG_centerX = button.SG_centerX;
  371. return;
  372. }
  373. if (self.configure.indicatorStyle == SGIndicatorStyleDynamic) {
  374. self.indicatorView.SG_width = self.configure.indicatorDynamicWidth;
  375. self.indicatorView.SG_centerX = button.SG_centerX;
  376. return;
  377. }
  378. CGSize tempSize = [self P_sizeWithString:button.currentTitle font:self.configure.titleFont];
  379. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  380. if (tempIndicatorWidth > button.SG_width) {
  381. tempIndicatorWidth = button.SG_width;
  382. }
  383. self.indicatorView.SG_width = tempIndicatorWidth;
  384. self.indicatorView.SG_centerX = button.SG_centerX;
  385. }];
  386. }
  387. #pragma mark - - - 给外界提供的方法
  388. - (void)setPageTitleViewWithProgress:(CGFloat)progress originalIndex:(NSInteger)originalIndex targetIndex:(NSInteger)targetIndex {
  389. // 1、取出 originalBtn、targetBtn
  390. UIButton *originalBtn = self.btnMArr[originalIndex];
  391. UIButton *targetBtn = self.btnMArr[targetIndex];
  392. _signBtnIndex = targetBtn.tag;
  393. // 2、标题滚动样式下选中标题居中处理
  394. if (self.allBtnWidth > SGPageTitleViewWidth) {
  395. if (_signBtnClick == NO) {
  396. [self P_selectedBtnCenter:targetBtn];
  397. }
  398. _signBtnClick = NO;
  399. }
  400. // 3、处理指示器的逻辑
  401. if (self.allBtnWidth <= self.bounds.size.width) { /// SGPageTitleView 静止样式
  402. if (self.configure.indicatorScrollStyle == SGIndicatorScrollStyleDefault) {
  403. [self P_staticIndicatorScrollStyleDefaultWithProgress:progress originalBtn:originalBtn targetBtn:targetBtn];
  404. } else {
  405. [self P_staticIndicatorScrollStyleHalfEndWithProgress:progress originalBtn:originalBtn targetBtn:targetBtn];
  406. }
  407. } else { /// SGPageTitleView 可滚动
  408. if (self.configure.indicatorScrollStyle == SGIndicatorScrollStyleDefault) {
  409. [self P_indicatorScrollStyleDefaultWithProgress:progress originalBtn:originalBtn targetBtn:targetBtn];
  410. } else {
  411. [self P_indicatorScrollStyleHalfEndWithProgress:progress originalBtn:originalBtn targetBtn:targetBtn];
  412. }
  413. }
  414. // 4、颜色的渐变(复杂)
  415. if (self.configure.titleGradientEffect == YES) {
  416. [self P_isTitleGradientEffectWithProgress:progress originalBtn:originalBtn targetBtn:targetBtn];
  417. }
  418. // 5 、标题文字缩放属性(开启文字选中字号属性将不起作用)
  419. UIFont *configureTitleSelectedFont = self.configure.titleSelectedFont;
  420. UIFont *defaultTitleFont = [UIFont systemFontOfSize:15];
  421. if ([configureTitleSelectedFont.fontName isEqualToString:defaultTitleFont.fontName] && configureTitleSelectedFont.pointSize == defaultTitleFont.pointSize) {
  422. if (self.configure.titleTextZoom == YES) {
  423. // originalBtn 缩放
  424. CGFloat originalBtnZoomRatio = (1 - progress) * self.configure.titleTextZoomRatio;
  425. originalBtn.transform = CGAffineTransformMakeScale(originalBtnZoomRatio + 1, originalBtnZoomRatio + 1);
  426. // targetBtn 缩放
  427. CGFloat targetBtnZoomRatio = progress * self.configure.titleTextZoomRatio;
  428. targetBtn.transform = CGAffineTransformMakeScale(targetBtnZoomRatio + 1, targetBtnZoomRatio + 1);
  429. }
  430. };
  431. }
  432. /** 根据下标值添加 badge */
  433. - (void)addBadgeForIndex:(NSInteger)index {
  434. UIButton *btn = self.btnMArr[index];
  435. UIView *badge = [[UIView alloc] init];
  436. CGFloat btnTextWidth = [self P_sizeWithString:btn.currentTitle font:self.configure.titleFont].width;
  437. CGFloat btnTextHeight = [self P_sizeWithString:btn.currentTitle font:self.configure.titleFont].height;
  438. CGFloat badgeX = 0.5 * (btn.SG_width - btnTextWidth) + btnTextWidth + self.configure.badgeOff.x;
  439. CGFloat badgeY = 0.5 * (btn.SG_height - btnTextHeight) + self.configure.badgeOff.y - self.configure.badgeSize;
  440. CGFloat badgeWidth = self.configure.badgeSize;
  441. CGFloat badgeHeight = badgeWidth;
  442. badge.frame = CGRectMake(badgeX, badgeY, badgeWidth, badgeHeight);
  443. badge.layer.backgroundColor = self.configure.badgeColor.CGColor;
  444. badge.layer.cornerRadius = 0.5 * self.configure.badgeSize;
  445. badge.tag = 2018 + index;
  446. [btn addSubview:badge];
  447. }
  448. /** 根据下标值移除 badge */
  449. - (void)removeBadgeForIndex:(NSInteger)index {
  450. UIButton *btn = self.btnMArr[index];
  451. [btn.subviews enumerateObjectsUsingBlock:^(__kindof UIView * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  452. if (obj.tag != 0) {
  453. [obj removeFromSuperview];
  454. obj = nil;
  455. }
  456. }];
  457. }
  458. /**
  459. * 根据标题下标值重置标题文字
  460. *
  461. * @param title 标题名
  462. * @param index 标题所对应的下标
  463. */
  464. - (void)resetTitle:(NSString *)title forIndex:(NSInteger)index {
  465. UIButton *button = (UIButton *)self.btnMArr[index];
  466. [button setTitle:title forState:UIControlStateNormal];
  467. if (_signBtnIndex == index) {
  468. if (self.configure.indicatorStyle == SGIndicatorStyleDefault || self.configure.indicatorStyle == SGIndicatorStyleCover) {
  469. CGSize tempSize = [self P_sizeWithString:button.currentTitle font:self.configure.titleFont];
  470. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  471. if (tempIndicatorWidth > button.SG_width) {
  472. tempIndicatorWidth = button.SG_width;
  473. }
  474. self.indicatorView.SG_width = tempIndicatorWidth;
  475. self.indicatorView.SG_centerX = button.SG_centerX;
  476. }
  477. }
  478. }
  479. /** 重置指示器颜色 */
  480. - (void)resetIndicatorColor:(UIColor *)color {
  481. _indicatorView.backgroundColor = color;
  482. }
  483. /**
  484. * 重置标题普通状态、选中状态下文字颜色及指示器颜色方法
  485. *
  486. * @param color 普通状态下标题文字颜色
  487. * @param selectedColor 选中状态下标题文字颜色
  488. */
  489. - (void)resetTitleColor:(UIColor *)color titleSelectedColor:(UIColor *)selectedColor {
  490. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  491. UIButton *btn = obj;
  492. [btn setTitleColor:color forState:(UIControlStateNormal)];
  493. [btn setTitleColor:selectedColor forState:(UIControlStateSelected)];
  494. }];
  495. }
  496. /**
  497. * 重置标题普通状态、选中状态下文字颜色及指示器颜色方法
  498. *
  499. * @param color 普通状态下标题文字颜色
  500. * @param selectedColor 选中状态下标题文字颜色
  501. * @param indicatorColor 指示器颜色
  502. */
  503. - (void)resetTitleColor:(UIColor *)color titleSelectedColor:(UIColor *)selectedColor indicatorColor:(UIColor *)indicatorColor {
  504. [self resetTitleColor:color titleSelectedColor:selectedColor];
  505. [self resetIndicatorColor:indicatorColor];
  506. }
  507. /**
  508. * 根据标题下标值设置标题的 attributedTitle 属性
  509. *
  510. * @param attributedTitle attributedTitle 属性
  511. * @param selectedAttributedTitle 选中状态下 attributedTitle 属性
  512. * @param index 标题所对应的下标
  513. */
  514. - (void)setAttributedTitle:(NSMutableAttributedString *)attributedTitle selectedAttributedTitle:(NSMutableAttributedString *)selectedAttributedTitle forIndex:(NSInteger)index {
  515. UIButton *button = (UIButton *)self.btnMArr[index];
  516. [button setAttributedTitle:attributedTitle forState:(UIControlStateNormal)];
  517. [button setAttributedTitle:selectedAttributedTitle forState:(UIControlStateSelected)];
  518. }
  519. /**
  520. * 设置标题图片及位置样式
  521. *
  522. * @param images 默认图片名数组
  523. * @param selectedImages 选中图片名数组
  524. * @param imagePositionType 图片位置样式
  525. * @param spacing 图片与标题文字之间的间距
  526. */
  527. - (void)setImages:(NSArray *)images selectedImages:(NSArray *)selectedImages imagePositionType:(SGImagePositionType)imagePositionType spacing:(CGFloat)spacing {
  528. NSInteger imagesCount = images.count;
  529. NSInteger selectedImagesCount = selectedImages.count;
  530. NSInteger titlesCount = self.titleArr.count;
  531. if (imagesCount < selectedImagesCount) {
  532. NSLog(@"温馨提示:SGPageTitleView -> [setImages:selectedImages:imagePositionType:spacing] 方法中 images 必须大于或者等于selectedImages,否则 imagePositionTypeDefault 以外的其他样式图片及文字布局将会出现问题");
  533. }
  534. if (imagesCount < titlesCount) {
  535. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  536. UIButton *btn = obj;
  537. if (idx >= imagesCount - 1) {
  538. *stop = YES;
  539. }
  540. [self P_btn:btn imageName:images[idx] imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateNormal)];
  541. }];
  542. } else {
  543. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  544. UIButton *btn = obj;
  545. [self P_btn:btn imageName:images[idx] imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateNormal)];
  546. }];
  547. }
  548. if (selectedImagesCount < titlesCount) {
  549. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  550. UIButton *btn = obj;
  551. if (idx >= selectedImagesCount - 1) {
  552. *stop = YES;
  553. }
  554. [self P_btn:btn imageName:selectedImages[idx] imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateSelected)];
  555. }];
  556. } else {
  557. [self.btnMArr enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  558. UIButton *btn = obj;
  559. [self P_btn:btn imageName:selectedImages[idx] imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateSelected)];
  560. }];
  561. }
  562. }
  563. /**
  564. * 根据标题下标设置标题图片及位置样式
  565. *
  566. * @param image 默认图片名
  567. * @param selectedImage 选中时图片名
  568. * @param imagePositionType 图片位置样式
  569. * @param spacing 图片与标题文字之间的间距
  570. * @param index 标题对应下标值
  571. */
  572. - (void)setImage:(NSString *)image selectedImage:(NSString *)selectedImage imagePositionType:(SGImagePositionType)imagePositionType spacing:(CGFloat)spacing forIndex:(NSInteger)index {
  573. UIFont *configureTitleFont = self.configure.titleFont;
  574. UIFont *configureTitleSelectedFont = self.configure.titleSelectedFont;
  575. if ([configureTitleFont.fontName isEqualToString:configureTitleSelectedFont.fontName] && configureTitleFont.pointSize == configureTitleSelectedFont.pointSize) {
  576. UIButton *btn = self.btnMArr[index];
  577. if (image != nil) {
  578. [self P_btn:btn imageName:image imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateNormal)];
  579. }
  580. if (selectedImage != nil) {
  581. [self P_btn:btn imageName:selectedImage imagePositionType:imagePositionType spacing:spacing btnControlState:(UIControlStateSelected)];
  582. }
  583. return;
  584. }
  585. NSLog(@"配置属性 titleFont 必须与配置属性 titleSelectedFont 一致,否则 setImage:selectedImage:imagePositionType:spacing:forIndex 方法将不起任何作用");
  586. }
  587. /// imagePositionType 样式设置方法抽取
  588. - (void)P_btn:(UIButton *)btn imageName:(NSString *)imageName imagePositionType:(SGImagePositionType)imagePositionType spacing:(CGFloat)spacing btnControlState:(UIControlState)btnControlState {
  589. if (imagePositionType == SGImagePositionTypeDefault) {
  590. [btn SG_imagePositionStyle:SGImagePositionStyleDefault spacing:spacing imagePositionBlock:^(UIButton *button) {
  591. [btn setImage:[UIImage imageNamed:imageName] forState:btnControlState];
  592. }];
  593. return;
  594. }
  595. if (imagePositionType == SGImagePositionTypeRight) {
  596. [btn SG_imagePositionStyle:SGImagePositionStyleRight spacing:spacing imagePositionBlock:^(UIButton *button) {
  597. [btn setImage:[UIImage imageNamed:imageName] forState:btnControlState];
  598. }];
  599. return;
  600. }
  601. if (imagePositionType == SGImagePositionTypeTop) {
  602. [btn SG_imagePositionStyle:SGImagePositionStyleTop spacing:spacing imagePositionBlock:^(UIButton *button) {
  603. [btn setImage:[UIImage imageNamed:imageName] forState:btnControlState];
  604. }];
  605. return;
  606. }
  607. if (imagePositionType == SGImagePositionTypeBottom) {
  608. [btn SG_imagePositionStyle:SGImagePositionStyleBottom spacing:spacing imagePositionBlock:^(UIButton *button) {
  609. [btn setImage:[UIImage imageNamed:imageName] forState:btnControlState];
  610. }];
  611. }
  612. }
  613. #pragma mark - - - SGPageTitleView 静止样式下指示器默认滚动样式(SGIndicatorScrollStyleDefault)
  614. - (void)P_staticIndicatorScrollStyleDefaultWithProgress:(CGFloat)progress originalBtn:(UIButton *)originalBtn targetBtn:(UIButton *)targetBtn {
  615. // 改变按钮的选择状态
  616. if (progress >= 0.8) { /// 此处取 >= 0.8 而不是 1.0 为的是防止用户滚动过快而按钮的选中状态并没有改变
  617. [self P_changeSelectedButton:targetBtn];
  618. }
  619. /// 处理 SGIndicatorStyleFixed 样式
  620. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  621. CGFloat btnWidth = self.SG_width / self.titleArr.count;
  622. CGFloat targetBtnMaxX = (targetBtn.tag + 1) * btnWidth;
  623. CGFloat originalBtnMaxX = (originalBtn.tag + 1) * btnWidth;
  624. CGFloat targetBtnIndicatorX = targetBtnMaxX - 0.5 * (btnWidth - self.configure.indicatorFixedWidth) - self.configure.indicatorFixedWidth;
  625. CGFloat originalBtnIndicatorX = originalBtnMaxX - 0.5 * (btnWidth - self.configure.indicatorFixedWidth) - self.configure.indicatorFixedWidth;
  626. CGFloat totalOffsetX = targetBtnIndicatorX - originalBtnIndicatorX;
  627. self.indicatorView.SG_x = originalBtnIndicatorX + progress * totalOffsetX;
  628. return;
  629. }
  630. /// 处理 SGIndicatorStyleDynamic 样式
  631. if (self.configure.indicatorStyle == SGIndicatorStyleDynamic) {
  632. NSInteger originalBtnTag = originalBtn.tag;
  633. NSInteger targetBtnTag = targetBtn.tag;
  634. CGFloat btnWidth = self.SG_width / self.titleArr.count;
  635. CGFloat targetBtnMaxX = (targetBtn.tag + 1) * btnWidth;;
  636. CGFloat originalBtnMaxX = (originalBtn.tag + 1) * btnWidth;
  637. if (originalBtnTag <= targetBtnTag) { // 往左滑
  638. if (progress <= 0.5) {
  639. self.indicatorView.SG_width = self.configure.indicatorDynamicWidth + 2 * progress * btnWidth;
  640. } else {
  641. CGFloat targetBtnIndicatorX = targetBtnMaxX - 0.5 * (btnWidth - self.configure.indicatorDynamicWidth) - self.configure.indicatorDynamicWidth;
  642. self.indicatorView.SG_x = targetBtnIndicatorX + 2 * (progress - 1) * btnWidth;
  643. self.indicatorView.SG_width = self.configure.indicatorDynamicWidth + 2 * (1 - progress) * btnWidth;
  644. }
  645. } else {
  646. if (progress <= 0.5) {
  647. CGFloat originalBtnIndicatorX = originalBtnMaxX - 0.5 * (btnWidth - self.configure.indicatorDynamicWidth) - self.configure.indicatorDynamicWidth;
  648. self.indicatorView.SG_x = originalBtnIndicatorX - 2 * progress * btnWidth;
  649. self.indicatorView.SG_width = self.configure.indicatorDynamicWidth + 2 * progress * btnWidth;
  650. } else {
  651. CGFloat targetBtnIndicatorX = targetBtnMaxX - self.configure.indicatorDynamicWidth - 0.5 * (btnWidth - self.configure.indicatorDynamicWidth);
  652. self.indicatorView.SG_x = targetBtnIndicatorX; // 这句代码必须写,防止滚动结束之后指示器位置存在偏差,这里的偏差是由于 progress >= 0.8 导致的
  653. self.indicatorView.SG_width = self.configure.indicatorDynamicWidth + 2 * (1 - progress) * btnWidth;
  654. }
  655. }
  656. return;
  657. }
  658. /// 处理指示器下划线、遮盖样式
  659. CGFloat btnWidth = self.SG_width / self.titleArr.count;
  660. // 文字宽度
  661. CGFloat targetBtnTextWidth = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont].width;
  662. CGFloat originalBtnTextWidth = [self P_sizeWithString:originalBtn.currentTitle font:self.configure.titleFont].width;
  663. CGFloat targetBtnMaxX = 0.0;
  664. CGFloat originalBtnMaxX = 0.0;
  665. /// 这里的缩放是标题按钮缩放,按钮的 frame 会发生变化,开启缩放性后,如果指示器还使用 CGRectGetMaxX 获取按钮的最大 X 值是会比之前的值大,这样会导致指示器的位置相对按钮位置不对应(存在一定的偏移);所以这里根据按钮下标计算原本的 CGRectGetMaxX 的值,缩放后的不去理会,这样指示器位置会与按钮位置保持一致。
  666. /// 在缩放属性关闭情况下,下面的计算结果一样的,所以可以省略判断,直接采用第一种计算结果(这个只是做个记录对指示器位置与按钮保持一致的方法)
  667. if (self.configure.titleTextZoom == YES) {
  668. targetBtnMaxX = (targetBtn.tag + 1) * btnWidth;
  669. originalBtnMaxX = (originalBtn.tag + 1) * btnWidth;
  670. } else {
  671. targetBtnMaxX = CGRectGetMaxX(targetBtn.frame);
  672. originalBtnMaxX = CGRectGetMaxX(originalBtn.frame);
  673. }
  674. CGFloat targetIndicatorX = targetBtnMaxX - targetBtnTextWidth - 0.5 * (btnWidth - targetBtnTextWidth + self.configure.indicatorAdditionalWidth);
  675. CGFloat originalIndicatorX = originalBtnMaxX - originalBtnTextWidth - 0.5 * (btnWidth - originalBtnTextWidth + self.configure.indicatorAdditionalWidth);
  676. CGFloat totalOffsetX = targetIndicatorX - originalIndicatorX;
  677. /// 2、计算文字之间差值
  678. // targetBtn 文字右边的 x 值
  679. CGFloat targetBtnRightTextX = targetBtnMaxX - 0.5 * (btnWidth - targetBtnTextWidth);
  680. // originalBtn 文字右边的 x 值
  681. CGFloat originalBtnRightTextX = originalBtnMaxX - 0.5 * (btnWidth - originalBtnTextWidth);
  682. CGFloat totalRightTextDistance = targetBtnRightTextX - originalBtnRightTextX;
  683. // 计算 indicatorView 滚动时 x 的偏移量
  684. CGFloat offsetX = totalOffsetX * progress;
  685. // 计算 indicatorView 滚动时文字宽度的偏移量
  686. CGFloat distance = progress * (totalRightTextDistance - totalOffsetX);
  687. /// 3、计算 indicatorView 新的 frame
  688. self.indicatorView.SG_x = originalIndicatorX + offsetX;
  689. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + originalBtnTextWidth + distance;
  690. if (tempIndicatorWidth >= targetBtn.SG_width) {
  691. CGFloat moveTotalX = targetBtn.SG_origin.x - originalBtn.SG_origin.x;
  692. CGFloat moveX = moveTotalX * progress;
  693. self.indicatorView.SG_centerX = originalBtn.SG_centerX + moveX;
  694. } else {
  695. self.indicatorView.SG_width = tempIndicatorWidth;
  696. }
  697. }
  698. #pragma mark - - - SGPageTitleView 滚动样式下指示器默认滚动样式(SGIndicatorScrollStyleDefault)
  699. - (void)P_indicatorScrollStyleDefaultWithProgress:(CGFloat)progress originalBtn:(UIButton *)originalBtn targetBtn:(UIButton *)targetBtn {
  700. /// 改变按钮的选择状态
  701. if (progress >= 0.8) { /// 此处取 >= 0.8 而不是 1.0 为的是防止用户滚动过快而按钮的选中状态并没有改变
  702. [self P_changeSelectedButton:targetBtn];
  703. }
  704. /// 处理 SGIndicatorStyleFixed 样式
  705. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  706. CGFloat targetIndicatorX = CGRectGetMaxX(targetBtn.frame) - 0.5 * (targetBtn.SG_width - self.configure.indicatorFixedWidth) - self.configure.indicatorFixedWidth;
  707. CGFloat originalIndicatorX = CGRectGetMaxX(originalBtn.frame) - self.configure.indicatorFixedWidth - 0.5 * (originalBtn.SG_width - self.configure.indicatorFixedWidth);
  708. CGFloat totalOffsetX = targetIndicatorX - originalIndicatorX;
  709. CGFloat offsetX = totalOffsetX * progress;
  710. self.indicatorView.SG_x = originalIndicatorX + offsetX;
  711. return;
  712. }
  713. /// 处理 SGIndicatorStyleDynamic 样式
  714. if (self.configure.indicatorStyle == SGIndicatorStyleDynamic) {
  715. NSInteger originalBtnTag = originalBtn.tag;
  716. NSInteger targetBtnTag = targetBtn.tag;
  717. if (originalBtnTag <= targetBtnTag) { // 往左滑
  718. // targetBtn 与 originalBtn 中心点之间的距离
  719. CGFloat btnCenterXDistance = targetBtn.SG_centerX - originalBtn.SG_centerX;
  720. if (progress <= 0.5) {
  721. self.indicatorView.SG_width = 2 * progress * btnCenterXDistance + self.configure.indicatorDynamicWidth;
  722. } else {
  723. CGFloat targetBtnX = CGRectGetMaxX(targetBtn.frame) - self.configure.indicatorDynamicWidth - 0.5 * (targetBtn.SG_width - self.configure.indicatorDynamicWidth);
  724. self.indicatorView.SG_x = targetBtnX + 2 * (progress - 1) * btnCenterXDistance;
  725. self.indicatorView.SG_width = 2 * (1 - progress) * btnCenterXDistance + self.configure.indicatorDynamicWidth;
  726. }
  727. } else {
  728. // originalBtn 与 targetBtn 中心点之间的距离
  729. CGFloat btnCenterXDistance = originalBtn.SG_centerX - targetBtn.SG_centerX;
  730. if (progress <= 0.5) {
  731. CGFloat originalBtnX = CGRectGetMaxX(originalBtn.frame) - self.configure.indicatorDynamicWidth - 0.5 * (originalBtn.SG_width - self.configure.indicatorDynamicWidth);
  732. self.indicatorView.SG_x = originalBtnX - 2 * progress * btnCenterXDistance;
  733. self.indicatorView.SG_width = 2 * progress * btnCenterXDistance + self.configure.indicatorDynamicWidth;
  734. } else {
  735. CGFloat targetBtnX = CGRectGetMaxX(targetBtn.frame) - self.configure.indicatorDynamicWidth - 0.5 * (targetBtn.SG_width - self.configure.indicatorDynamicWidth);
  736. self.indicatorView.SG_x = targetBtnX; // 这句代码必须写,防止滚动结束之后指示器位置存在偏差,这里的偏差是由于 progress >= 0.8 导致的
  737. self.indicatorView.SG_width = 2 * (1 - progress) * btnCenterXDistance + self.configure.indicatorDynamicWidth;
  738. }
  739. }
  740. return;
  741. }
  742. /// 处理指示器下划线、遮盖样式
  743. if (self.configure.titleTextZoom && self.configure.showIndicator) {
  744. NSLog(@"标题文字缩放属性与指示器下划线、遮盖样式下不兼容,但固定及动态样式下兼容");
  745. return;
  746. }
  747. // 1、计算 targetBtn 与 originalBtn 之间的 x 差值
  748. CGFloat totalOffsetX = targetBtn.SG_x - originalBtn.SG_x;
  749. // 2、计算 targetBtn 与 originalBtn 之间距离的差值
  750. CGFloat totalDistance = CGRectGetMaxX(targetBtn.frame) - CGRectGetMaxX(originalBtn.frame);
  751. /// 计算 indicator 滚动时 x 的偏移量
  752. CGFloat offsetX = 0.0;
  753. /// 计算 indicator 滚动时宽度的偏移量
  754. CGFloat distance = 0.0;
  755. CGFloat targetBtnTextWidth = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont].width;
  756. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + targetBtnTextWidth;
  757. if (tempIndicatorWidth >= targetBtn.SG_width) {
  758. offsetX = totalOffsetX * progress;
  759. distance = progress * (totalDistance - totalOffsetX);
  760. self.indicatorView.SG_x = originalBtn.SG_x + offsetX;
  761. self.indicatorView.SG_width = originalBtn.SG_width + distance;
  762. } else {
  763. offsetX = totalOffsetX * progress + 0.5 * self.configure.titleAdditionalWidth - 0.5 * self.configure.indicatorAdditionalWidth;
  764. distance = progress * (totalDistance - totalOffsetX) - self.configure.titleAdditionalWidth;
  765. /// 计算 indicator 新的 frame
  766. self.indicatorView.SG_x = originalBtn.SG_x + offsetX;
  767. self.indicatorView.SG_width = originalBtn.SG_width + distance + self.configure.indicatorAdditionalWidth;
  768. }
  769. }
  770. #pragma mark - - - SGPageTitleView 静止样式下指示器 SGIndicatorScrollStyleHalf 和 SGIndicatorScrollStyleEnd 滚动样式
  771. - (void)P_staticIndicatorScrollStyleHalfEndWithProgress:(CGFloat)progress originalBtn:(UIButton *)originalBtn targetBtn:(UIButton *)targetBtn {
  772. /// 1、处理 SGIndicatorScrollStyleHalf 逻辑
  773. if (self.configure.indicatorScrollStyle == SGIndicatorScrollStyleHalf) {
  774. // 1、处理 SGIndicatorStyleFixed 样式
  775. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  776. if (progress >= 0.5) {
  777. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  778. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  779. [self P_changeSelectedButton:targetBtn];
  780. }];
  781. } else {
  782. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  783. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  784. [self P_changeSelectedButton:originalBtn];
  785. }];
  786. }
  787. return;
  788. }
  789. // 2、处理指示器下划线、遮盖样式
  790. if (progress >= 0.5) {
  791. CGSize tempSize = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont];
  792. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  793. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  794. if (tempIndicatorWidth >= targetBtn.SG_width) {
  795. self.indicatorView.SG_width = targetBtn.SG_width;
  796. } else {
  797. self.indicatorView.SG_width = tempIndicatorWidth;
  798. }
  799. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  800. [self P_changeSelectedButton:targetBtn];
  801. }];
  802. } else {
  803. CGSize tempSize = [self P_sizeWithString:originalBtn.currentTitle font:self.configure.titleFont];
  804. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  805. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  806. if (tempIndicatorWidth >= targetBtn.SG_width) {
  807. self.indicatorView.SG_width = originalBtn.SG_width;
  808. } else {
  809. self.indicatorView.SG_width = tempIndicatorWidth;
  810. }
  811. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  812. [self P_changeSelectedButton:originalBtn];
  813. }];
  814. }
  815. return;
  816. }
  817. /// 2、处理 SGIndicatorScrollStyleEnd 逻辑
  818. // 1、处理 SGIndicatorStyleFixed 样式
  819. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  820. if (progress == 1.0) {
  821. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  822. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  823. [self P_changeSelectedButton:targetBtn];
  824. }];
  825. } else {
  826. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  827. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  828. [self P_changeSelectedButton:originalBtn];
  829. }];
  830. }
  831. return;
  832. }
  833. // 2、处理指示器下划线、遮盖样式
  834. if (progress == 1.0) {
  835. CGSize tempSize = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont];
  836. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  837. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  838. if (tempIndicatorWidth >= targetBtn.SG_width) {
  839. self.indicatorView.SG_width = targetBtn.SG_width;
  840. } else {
  841. self.indicatorView.SG_width = tempIndicatorWidth;
  842. }
  843. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  844. [self P_changeSelectedButton:targetBtn];
  845. }];
  846. } else {
  847. CGSize tempSize = [self P_sizeWithString:originalBtn.currentTitle font:self.configure.titleFont];
  848. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  849. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  850. if (tempIndicatorWidth >= targetBtn.SG_width) {
  851. self.indicatorView.SG_width = originalBtn.SG_width;
  852. } else {
  853. self.indicatorView.SG_width = tempIndicatorWidth;
  854. }
  855. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  856. [self P_changeSelectedButton:originalBtn];
  857. }];
  858. }
  859. }
  860. #pragma mark - - - SGPageTitleView 滚动样式下指示器 SGIndicatorScrollStyleHalf 和 SGIndicatorScrollStyleEnd 滚动样式
  861. - (void)P_indicatorScrollStyleHalfEndWithProgress:(CGFloat)progress originalBtn:(UIButton *)originalBtn targetBtn:(UIButton *)targetBtn {
  862. /// 1、处理 SGIndicatorScrollStyleHalf 逻辑
  863. if (self.configure.indicatorScrollStyle == SGIndicatorScrollStyleHalf) {
  864. // 1、处理 SGIndicatorStyleFixed 样式
  865. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  866. if (progress >= 0.5) {
  867. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  868. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  869. [self P_changeSelectedButton:targetBtn];
  870. }];
  871. } else {
  872. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  873. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  874. [self P_changeSelectedButton:originalBtn];
  875. }];
  876. }
  877. return;
  878. }
  879. // 2、处理指示器下划线、遮盖样式
  880. if (progress >= 0.5) {
  881. CGSize tempSize = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont];
  882. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  883. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  884. if (tempIndicatorWidth >= targetBtn.SG_width) {
  885. self.indicatorView.SG_width = targetBtn.SG_width;
  886. } else {
  887. self.indicatorView.SG_width = tempIndicatorWidth;
  888. }
  889. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  890. [self P_changeSelectedButton:targetBtn];
  891. }];
  892. } else {
  893. CGSize tempSize = [self P_sizeWithString:originalBtn.currentTitle font:self.configure.titleFont];
  894. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  895. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  896. if (tempIndicatorWidth >= originalBtn.SG_width) {
  897. self.indicatorView.SG_width = originalBtn.SG_width;
  898. } else {
  899. self.indicatorView.SG_width = tempIndicatorWidth;
  900. }
  901. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  902. [self P_changeSelectedButton:originalBtn];
  903. }];
  904. }
  905. return;
  906. }
  907. /// 2、处理 SGIndicatorScrollStyleEnd 逻辑
  908. // 1、处理 SGIndicatorStyleFixed 样式
  909. if (self.configure.indicatorStyle == SGIndicatorStyleFixed) {
  910. if (progress == 1.0) {
  911. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  912. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  913. [self P_changeSelectedButton:targetBtn];
  914. }];
  915. } else {
  916. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  917. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  918. [self P_changeSelectedButton:originalBtn];
  919. }];
  920. }
  921. return;
  922. }
  923. // 2、处理指示器下划线、遮盖样式
  924. if (progress == 1.0) {
  925. CGSize tempSize = [self P_sizeWithString:targetBtn.currentTitle font:self.configure.titleFont];
  926. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  927. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  928. if (tempIndicatorWidth >= targetBtn.SG_width) {
  929. self.indicatorView.SG_width = targetBtn.SG_width;
  930. } else {
  931. self.indicatorView.SG_width = tempIndicatorWidth;
  932. }
  933. self.indicatorView.SG_centerX = targetBtn.SG_centerX;
  934. [self P_changeSelectedButton:targetBtn];
  935. }];
  936. } else {
  937. CGSize tempSize = [self P_sizeWithString:originalBtn.currentTitle font:self.configure.titleFont];
  938. CGFloat tempIndicatorWidth = self.configure.indicatorAdditionalWidth + tempSize.width;
  939. [UIView animateWithDuration:self.configure.indicatorAnimationTime animations:^{
  940. if (tempIndicatorWidth >= originalBtn.SG_width) {
  941. self.indicatorView.SG_width = originalBtn.SG_width;
  942. } else {
  943. self.indicatorView.SG_width = tempIndicatorWidth;
  944. }
  945. self.indicatorView.SG_centerX = originalBtn.SG_centerX;
  946. [self P_changeSelectedButton:originalBtn];
  947. }];
  948. }
  949. }
  950. #pragma mark - - - 颜色渐变方法抽取
  951. - (void)P_isTitleGradientEffectWithProgress:(CGFloat)progress originalBtn:(UIButton *)originalBtn targetBtn:(UIButton *)targetBtn {
  952. // 获取 targetProgress
  953. CGFloat targetProgress = progress;
  954. // 获取 originalProgress
  955. CGFloat originalProgress = 1 - targetProgress;
  956. CGFloat r = self.endR - self.startR;
  957. CGFloat g = self.endG - self.startG;
  958. CGFloat b = self.endB - self.startB;
  959. UIColor *originalColor = [UIColor colorWithRed:self.startR + r * originalProgress green:self.startG + g * originalProgress blue:self.startB + b * originalProgress alpha:1];
  960. UIColor *targetColor = [UIColor colorWithRed:self.startR + r * targetProgress green:self.startG + g * targetProgress blue:self.startB + b * targetProgress alpha:1];
  961. // 设置文字颜色渐变
  962. originalBtn.titleLabel.textColor = originalColor;
  963. targetBtn.titleLabel.textColor = targetColor;
  964. }
  965. #pragma mark - - - set
  966. - (void)setResetSelectedIndex:(NSInteger)resetSelectedIndex {
  967. _resetSelectedIndex = resetSelectedIndex;
  968. [self P_btn_action:self.btnMArr[resetSelectedIndex]];
  969. }
  970. #pragma mark - - - 颜色设置的计算
  971. /// 开始颜色设置
  972. - (void)setupStartColor:(UIColor *)color {
  973. CGFloat components[3];
  974. [self P_getRGBComponents:components forColor:color];
  975. self.startR = components[0];
  976. self.startG = components[1];
  977. self.startB = components[2];
  978. }
  979. /// 结束颜色设置
  980. - (void)setupEndColor:(UIColor *)color {
  981. CGFloat components[3];
  982. [self P_getRGBComponents:components forColor:color];
  983. self.endR = components[0];
  984. self.endG = components[1];
  985. self.endB = components[2];
  986. }
  987. /**
  988. * 指定颜色,获取颜色的RGB值
  989. *
  990. * @param components RGB数组
  991. * @param color 颜色
  992. */
  993. - (void)P_getRGBComponents:(CGFloat [3])components forColor:(UIColor *)color {
  994. CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
  995. unsigned char resultingPixel[4];
  996. CGContextRef context = CGBitmapContextCreate(&resultingPixel, 1, 1, 8, 4, rgbColorSpace, 1);
  997. CGContextSetFillColorWithColor(context, [color CGColor]);
  998. CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
  999. CGContextRelease(context);
  1000. CGColorSpaceRelease(rgbColorSpace);
  1001. for (int component = 0; component < 3; component++) {
  1002. components[component] = resultingPixel[component] / 255.0f;
  1003. }
  1004. }
  1005. @end