国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > iOS > 正文

iOS自動進行View標記的方法詳解

2020-07-26 02:20:44
字體:
來源:轉載
供稿:網友

緣起

一切都源于我的上一篇博客,我寫的是一篇 UITableViewCell使用自動布局的“最佳實踐” ,我需要給我的圖片里面的UIView元素添加上邊距的標記,這讓我感到很為難,我覺得我得發點時間寫一個程序讓這個步驟自動化,我只要一鍵就能讓我的程序自動標記邊距,這個比我要手動去標記來的酷很多不是嗎!

結果

所以,我發了點時間實現了我的想法,下面是實現的結果截圖:

以及代碼開源托管地址:代碼鏈接 (本地下載)

預覽圖

過去幾小時內的想法

靜下心來整理我的想法和尋找方案,大概的整理下了一個可行性的方案以及這個方案中需要使用到的步驟,其中一些細節沒有在這個步驟中體現

  • 獲取水平的間距:遍歷父View的子View,獲取某個子sourceView的右邊到其他子targetView的左邊的距離,把結果保存到子targetView的入度數組中
  • 獲取垂直的間距:遍歷父View的子View,獲取某個子sourceView的下邊到其他子targetView的上邊的距離,把結果保存到子targetView的入度數組中
  • 篩選出targetView的入度數組中所以不符合的結果,刪除這些結果
  • 最終獲取到了一個需要進行展示的結果數組,數組保存的是一系列的間距線段對象
  • 創建一個顯示標記的TagView層,把結果的線段繪制在TagView上面,然后把TabView添加到父View上

代碼實現解析

注入測試邊框View

我的方案中所有的間距都是基于子View考慮的,所以子View和父View的邊距需要特殊的計算,可以使用在父View的旁邊添加一個物理像素的子View,最終只要處理所有這些子View,子View和父View的邊距就能得到體現了,不用再做多余的處理,這是一個討巧的方案。

+ (void)registerBorderTestViewWithView:(UIView*)view { CGFloat minWH = 1.0/[UIScreen mainScreen].scale; MMBorderAttachView* leftBorderView = [[MMBorderAttachView alloc] initWithFrame:CGRectMake(0, 0, minWH, view.bounds.size.height)]; [view addSubview:leftBorderView]; MMBorderAttachView* rightBorderView = [[MMBorderAttachView alloc] initWithFrame:CGRectMake(view.bounds.size.width-minWH, 0, minWH, view.bounds.size.height)]; [view addSubview:rightBorderView];  MMBorderAttachView* topBorderView = [[MMBorderAttachView alloc] initWithFrame:CGRectMake(0, 0, view.bounds.size.width, minWH)]; [view addSubview:topBorderView]; MMBorderAttachView* bottomBorderView = [[MMBorderAttachView alloc] initWithFrame:CGRectMake(0, view.bounds.size.height - minWH, view.bounds.size.width, minWH)]; [view addSubview:bottomBorderView];}

獲取父View的所有子View,抽象為MMFrameObject對象

NSMutableArray* viewFrameObjs = [NSMutableArray array]; NSArray* subViews = view.subviews; for (UIView* subView in subViews) {  // 過濾特殊的View,不屬于注入的View  if (![subView conformsToProtocol:@protocol(MMAbstractView)]) {   if (subView.alpha<0.001f) {    continue;   }      if (subView.frame.size.height <= 2) {    continue;   }  }    MMFrameObject* frameObj = [[MMFrameObject alloc] init];  frameObj.frame = subView.frame;  frameObj.attachedView = subView;  [viewFrameObjs addObject:frameObj]; }

獲取View之間的間距

需要處理兩種情況:1、尋找View的右邊對應的其他View的左邊;2、尋找View的下邊對應的其他View的上邊,特殊滴需要處理兩者都是MMAbstractView的情況,這種不需要處理

NSMutableArray<MMLine*>* lines = [NSMutableArray array]; for (MMFrameObject* sourceFrameObj in viewFrameObjs) {  for (MMFrameObject* targetFrameObj in viewFrameObjs) {      // 過濾特殊的View   if ([sourceFrameObj.attachedView conformsToProtocol:@protocol(MMAbstractView)]    && [targetFrameObj.attachedView conformsToProtocol:@protocol(MMAbstractView)]) {    continue;   }      // 尋找View的右邊對應的其他View的左邊   MMLine* hLine = [self horizontalLineWithFrameObj1:sourceFrameObj frameObj2:targetFrameObj];   if (hLine) {    [lines addObject:hLine];    [targetFrameObj.leftInjectedObjs addObject:hLine];   }      // 尋找View的下邊對應的其他View的上邊   MMLine* vLine = [self verticalLineWithFrameObj1:sourceFrameObj frameObj2:targetFrameObj];   if (vLine) {    [lines addObject:vLine];    [targetFrameObj.topInjectedObjs addObject:vLine];   }  } }

獲取間距線段的實現

以獲取水平的間距線段為例,這種情況,只需要處理一個子View在另一個子View的右邊的情況,否則返回nil跳過。獲取水平間距線段,明顯的線段的X軸是確定的,要,只要處理好Y軸就行了,問題就抽象為了兩個線段的問題,這部分是在approperiatePointWithInternal方法中處理的,主要步驟是一長的線段為標準,然后枚舉短的線段和長的線段存在的5種情況,相應的計算合適的值,然后給Y軸使用。

兩條線段的5種關系

+ (MMLine*)horizontalLineWithFrameObj1:(MMFrameObject*)frameObj1 frameObj2:(MMFrameObject*)frameObj2 { if (ABS(frameObj1.frame.origin.x - frameObj2.frame.origin.x) < 3) {  return nil; }  // frameObj2整體在frameObj1右邊 if (frameObj1.frame.origin.x + frameObj1.frame.size.width >= frameObj2.frame.origin.x) {  return nil; }  CGFloat obj1RightX = frameObj1.frame.origin.x + frameObj1.frame.size.width; CGFloat obj1Height = frameObj1.frame.size.height;  CGFloat obj2LeftX = frameObj2.frame.origin.x; CGFloat obj2Height = frameObj2.frame.size.height;  CGFloat handle = 0; CGFloat pointY = [self approperiatePointWithInternal:[[MMInterval alloc] initWithStart:frameObj1.frame.origin.y length:obj1Height] internal2:[[MMInterval alloc] initWithStart:frameObj2.frame.origin.y length:obj2Height] handle:&handle];  MMLine* line = [[MMLine alloc] initWithPoint1:[[MMShortPoint alloc] initWithX:obj1RightX y:pointY handle:handle] point2:[[MMShortPoint alloc] initWithX:obj2LeftX y:pointY handle:handle]];  return line;}+ (CGFloat)approperiatePointWithInternal:(MMInterval*)internal1 internal2:(MMInterval*)internal2 handle:(CGFloat*)handle { CGFloat MINHandleValue = 20; CGFloat pointValue = 0; CGFloat handleValue = 0; MMInterval* bigInternal; MMInterval* smallInternal; if (internal1.length > internal2.length) {  bigInternal = internal1;  smallInternal = internal2; } else {  bigInternal = internal2;  smallInternal = internal1; }  // 線段分割法 if (smallInternal.start < bigInternal.start && smallInternal.start+smallInternal.length < bigInternal.start) {  CGFloat tmpHandleValue = bigInternal.start - smallInternal.start+smallInternal.length;  pointValue = bigInternal.start - tmpHandleValue/2;  handleValue = MAX(tmpHandleValue, MINHandleValue); } if (smallInternal.start < bigInternal.start && smallInternal.start+smallInternal.length >= bigInternal.start) {  CGFloat tmpHandleValue = smallInternal.start+smallInternal.length - bigInternal.start;  pointValue = bigInternal.start + tmpHandleValue/2;  handleValue = MAX(tmpHandleValue, MINHandleValue); } if (smallInternal.start >= bigInternal.start && smallInternal.start+smallInternal.length <= bigInternal.start+bigInternal.length) {  CGFloat tmpHandleValue = smallInternal.length;  pointValue = smallInternal.start + tmpHandleValue/2;  handleValue = MAX(tmpHandleValue, MINHandleValue); } if (smallInternal.start >= bigInternal.start && smallInternal.start+smallInternal.length > bigInternal.start+bigInternal.length) {  CGFloat tmpHandleValue = bigInternal.start+bigInternal.length - smallInternal.start;  pointValue = bigInternal.start + tmpHandleValue/2;  handleValue = MAX(tmpHandleValue, MINHandleValue); } if (smallInternal.start >= bigInternal.start+bigInternal.length && smallInternal.start+smallInternal.length > bigInternal.start+bigInternal.length) {  CGFloat tmpHandleValue = smallInternal.start - (bigInternal.start+bigInternal.length);  pointValue = smallInternal.start - tmpHandleValue/2;  handleValue = MAX(tmpHandleValue, MINHandleValue); }  if (handle) {  *handle = handleValue; }  return pointValue;}

過濾線段

一個子View對象的入度可能有好幾個,需要篩選進行刪除,我使用的篩選策略是:以水平的間距線段為例,兩條線段的Y差值小于某個閾值,選擇線段長的那條刪除,最終獲取到了一個需要進行展示的結果數組,數組保存的是一系列的間距線段對象

// 查找重復的射入line // hLine:Y的差值小于某個值,leftInjectedObjs->取最小一條 // vLine:X的差值小于某個值,topInjectedObjs->取最小一條 CGFloat minValue = 5; for (MMFrameObject* sourceFrameObj in viewFrameObjs) {    {   // 排序:Y值:從大到小   [sourceFrameObj.leftInjectedObjs sortUsingComparator:^NSComparisonResult(MMLine* _Nonnull obj1, MMLine* _Nonnull obj2) {    return obj1.point1.point.y > obj2.point1.point.y;   }];   int i = 0;   NSLog(@"/n/n");   MMLine* baseLine, *compareLine;   if (sourceFrameObj.leftInjectedObjs.count) {    baseLine = sourceFrameObj.leftInjectedObjs[i];   }   while (i<sourceFrameObj.leftInjectedObjs.count) {    NSLog(@"lineWidth = %.1f == ", baseLine.lineWidth);    if (i + 1 < sourceFrameObj.leftInjectedObjs.count) {     compareLine = sourceFrameObj.leftInjectedObjs[i + 1];          if (ABS(baseLine.point1.point.y - compareLine.point1.point.y) < minValue) {      // 移除長的一條      if (baseLine.lineWidth > compareLine.lineWidth) {       [lines removeObject:baseLine];       baseLine = compareLine;      } else {       [lines removeObject:compareLine];      }     } else {      baseLine = compareLine;     }    }    i++;   }  }    {   // 排序:X值從大到小   [sourceFrameObj.topInjectedObjs sortUsingComparator:^NSComparisonResult(MMLine* _Nonnull obj1, MMLine* _Nonnull obj2) {    return obj1.point1.point.x >    obj2.point1.point.x;   }];   int j = 0;   MMLine* baseLine, *compareLine;   if (sourceFrameObj.topInjectedObjs.count) {    baseLine = sourceFrameObj.topInjectedObjs[j];   }   while (j<sourceFrameObj.topInjectedObjs.count) {    if (j + 1 < sourceFrameObj.topInjectedObjs.count) {     compareLine = sourceFrameObj.topInjectedObjs[j + 1];          if (ABS(baseLine.point1.point.x - compareLine.point1.point.x) < minValue) {      // 移除長的一條      // 移除長的一條      if (baseLine.lineWidth > compareLine.lineWidth) {       [lines removeObject:baseLine];       baseLine = compareLine;      } else {       [lines removeObject:compareLine];      }     } else {      baseLine = compareLine;     }    }    j++;   }  } }

TagView 的繪制

 // 繪制View TaggingView* taggingView = [[TaggingView alloc] initWithFrame:view.bounds lines:lines]; [view addSubview:taggingView];

TaggingView 在drawRect繪制線段以及線段長度的文字

//// TaggingView.m// AutolayoutCell//// Created by aron on 2017/5/27.// Copyright © 2017年 aron. All rights reserved.//#import "TaggingView.h"#import "MMTagModel.h"@interface TaggingView ()@property (nonatomic, strong) NSArray<MMLine*>* lines;;@end@implementation TaggingView- (instancetype)initWithFrame:(CGRect)frame lines:(NSArray<MMLine*>*)lines { self = [super initWithFrame:frame]; if (self) {  self.backgroundColor = [UIColor colorWithRed:255 green:255 blue:255 alpha:0.05];  _lines = lines; } return self;}- (void)drawRect:(CGRect)rect { [super drawRect:rect]; //1.獲取上下文 CGContextRef context = UIGraphicsGetCurrentContext();  for (MMLine* line in _lines) {  // 繪制線段  CGContextSetLineWidth(context, 2.0f/[UIScreen mainScreen].scale); //線寬  CGContextSetAllowsAntialiasing(context, true);  CGContextSetRGBStrokeColor(context, 255.0 / 255.0, 0.0 / 255.0, 70.0 / 255.0, 1.0); //線的顏色  CGContextBeginPath(context);  //設置起始點  CGContextMoveToPoint(context, line.point1.point.x, line.point1.point.y);  //增加點  CGContextAddLineToPoint(context, line.point2.point.x, line.point2.point.y);  CGContextStrokePath(context);    // 繪制文字  NSString *string = [NSString stringWithFormat:@"%.0f px", line.lineWidth];  UIFont *fount = [UIFont systemFontOfSize:7];  CGPoint centerPoint = line.centerPoint;  NSDictionary* attrDict = @{NSFontAttributeName : fount,        NSForegroundColorAttributeName: [UIColor redColor],        NSBackgroundColorAttributeName: [UIColor colorWithRed:1 green:1 blue:0 alpha:0.5f]};  [string drawInRect:CGRectMake(centerPoint.x - 15, centerPoint.y - 6, 30, 16) withAttributes:attrDict]; }}@end

以上就是我的的思路以及實現,有什么好的建議希望可以收到issue一起交流和談論。

代碼托管位置

代碼傳送門(本地下載)

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對武林網的支持。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 东阿县| 界首市| 龙海市| 台北市| 宜春市| 东港市| 墨玉县| 慈利县| 桦南县| 富源县| 伊宁县| 平罗县| 宜丰县| 吉林省| 六盘水市| 车致| 万州区| 新郑市| 高阳县| 铜梁县| 如皋市| 定西市| 东平县| 刚察县| 朝阳区| 武川县| 龙州县| 兴义市| 徐水县| 诸暨市| 永修县| 西乌珠穆沁旗| 湟中县| 武冈市| 贺兰县| 隆德县| 新丰县| 庆安县| 屏南县| 正安县| 南开区|