自定义遮罩视图,iosuiview对象实现

来源:http://www.smjxgs.com 作者:操作系统 人气:108 发布时间:2019-08-08
摘要:iOS 复制二个UIView对象完毕格局,iosuiview对象完成 iOS复制二个UIView对象,项目中要做一个菜鸟指引,也正是给有个别页面加个遮罩,然后卓越显示出一块视图,在写点提醒啥的。 本身的

iOS 复制二个UIView对象完毕格局,iosuiview对象完成

iOS 复制二个UIView对象,项目中要做一个菜鸟指引,也正是给有个别页面加个遮罩,然后卓越显示出一块视图,在写点提醒啥的。

本身的思路是加贰个同一的视图到遮罩上,不过难题来了,轻巧的视图是能够如此搞,复杂一点的难道也要创设多个一致的吧,那样岂不是太劳累了。

能还是不能够直接复制一份出来,然后加到遮罩上吧?

小编们都驾驭复制数组、字典、字符串这几个用copy,mutableCopy,然后自个儿就想当然的用自身的自定义View调了一晃copy,结果很为难,间接给崩了,说是要促成copyWithZone那个艺术。

总不能够具有的自定义View都以落到实处三回二个艺术呢!

网民们付出这么三个办法,作者喜笑貌开,赶紧拿去试一试

NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:view];  
UIView *copyView = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData];

然则这一个点子并不健全,在复制的copyView中,即使有所的子控件都能显得出来,可是地点都是nii,当然那不是主题素材的根本,最致命的是子控件里面包括圆角的话,复制出来后,子控件的圆角都么得啊。那就无法忍了,于是就有了那篇博客,作者想了笨方法,给复制出来的View的子控件,加上本该有的圆角!

一言不合就上代码:

@implementation GuideView

- (instancetype)init
{
    self = [super init];
    if (self) {
        self.backgroundColor = [UIColor colorWithWhite:0 alpha:0.3];
        self.frame = [UIScreen mainScreen].bounds;
        self.alpha = 0;
    }
    return self;
}

  (instancetype)guideView{
    return [[GuideView alloc] init];
}

- (void)addToView:(UIView *)view{
    [self addToView:view containCorner:NO];
}

/**  加给某个View上*/
- (void)addToView:(UIView *)view containCorner:(BOOL)containCorner{
    // 这样复制后,如果有子控件设置了圆角,复制后圆角会失效,而且所有子控件虽然都能显示正确,但地址都是nil
    NSData *archiveData = [NSKeyedArchiver archivedDataWithRootObject:view];
    UIView *copyView = [NSKeyedUnarchiver unarchiveObjectWithData:archiveData];

    // 子控件如果包含圆角
    if (containCorner == YES) {

        // 替换子控件
        NSData * subData = [NSKeyedArchiver archivedDataWithRootObject:view.subviews];
        NSArray* subViews = [NSKeyedUnarchiver unarchiveObjectWithData:subData];
        [copyView removeAllSubviews];
        for (UIView *subview in subViews) {
            [copyView addSubview:subview];
        }

        // 在原View中找到设置了圆角的子控件
        for (UIView *subView in view.subviews) {

            if (subView.layer.cornerRadius != 0)
            {
                // 在复制好的View的子控件中遍历,找到与之对应的子控件
                for (UIView *copySubView in copyView.subviews) {

                    // 给找到的控件设置上圆角,这里通过frame确定是否是同一个控件
                    if ([NSStringFromCGRect(copySubView.frame) isEqualToString:NSStringFromCGRect(subView.frame)]) {
                        copySubView.layer.cornerRadius = subView.layer.cornerRadius;
                        copySubView.layer.masksToBounds = YES;
                        break;
                    }
                }
            }
        }
    }
    copyView.frame = [view convertRect:view.bounds toView:kAppDelegate.window];
    [self addSubview:copyView];
}

- (void)show{
    [kAppDelegate.window addSubview:self];
    [UIView animateWithDuration:0.3 animations:^{
        self.alpha = 1;
    }];
}

- (void)disMiss{
    [UIView animateWithDuration:0.3 animations:^{
        self.alpha = 1;
    } completion:^(BOOL finished) {
          [self removeFromSuperview];
    }];
}

//- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
//    [super touchesBegan:touches withEvent:event];
//    [self disMiss];
//}
@end

效果图

4887王中王鉄算盘奖结果 1

:4887王中王鉄算盘奖结果 2

复制三个UIView对象达成形式,iosuiview对象完毕iOS 复制贰个UIView对象,项目中要做二个菜鸟教导,也正是给有些页面加个遮罩,然后杰出...

iOS开采- 自定义遮罩视图(引导, 成效表达)源码 分析

UIView(控件)

iOS开垦- 自定义遮罩视图(带领, 成效表达)源码 深入分析

咱俩平素使用App的时候, 平常在首先次使用的时候, 会有周边”新手教程”之类的东西, 来指引我们相应什么行使这么些App。

唯独那一个”新手教程”不相同于常规的指引页(引导页指第二回张开App时候, 弹出的这种介绍视图。 他是静态的, 无需与用户交互, 能够平昔一页页翻, 或许直接跳过。)所谓的”菜鸟教程”, 正是依照App的提醒, 一步步跟着完结。

那那几个“新手教程”有哪些平价呢?

指点用户驾驭操作。 比如, 如何调解高低, 怎样设置焦距… 限制用户填充须求消息, 实现供给操作。

前几天大家应该都晓得那终究是二个哪些事物了。为达成如此的功效, 一般是使用遮罩层效果, 明日花了点时间, 封装好了那般一个效果与利益。 同不寻常候把源码分享到自个儿的Github, 供给的能够谐和下载。

下载链接: YLZHoledView

再者, 还写了一个demo, 效果如下:  

自己封装好的那么些文件(YLZHoledView), 主要达成了如下效果:

  1. 充足各个体裁色盲灯效果。(圆形, 矩形, 圆角矩形, 自定义视图)
  2. 点击该区域援助delegate回调
  3. UIView 中的控件事件穿透。(demo中的开关, 是在遮罩层下方, 但是情有可原响应点击事件, 那正是事件穿透)

好了, 大意介绍就到那边, 上边轻便解释下中央代码:

UIView的创建

  1. alloc init方法成立
  • 通过UIStoryboard加载控件详见UIStoryboard
  • 通过xib加载控件详见XIB

delegate回调

YLZHoledView.h

@class YLZHoledView;
@protocol YLZHoledViewDelegate 

- (void)holedView:(YLZHoledView *)holedView didSelectHoleAtIndex:(NSUInteger)index;

@end

YLZHoledView.m

#pragma mark - Tap Gesture

 - (void)tapGestureDetectedForGesture:(UITapGestureRecognizer *)gesture
 {
     if ([self.holeViewDelegate respondsToSelector:@selector(holedView:didSelectHoleAtIndex:)]) {
         CGPoint touchLocation = [gesture locationInView:self];
         [self.holeViewDelegate holedView:self didSelectHoleAtIndex:[self holeViewIndexForAtPoint:touchLocation]];
     }
 }

那边先在自定义的View中增加手势, 然后在
- (NSUInteger)holeViewIndexForAtPoint:(CGPoint)touchLocation 中剖断点击范围是还是不是属于遮罩层中丰裕的视图, 再发送委托。

接下来在我们的兑现类中, 也便是完毕这些委托的地方, 就足以由此index来做连锁的操作。 作者demo里面只是轻便的打字与印刷。

UIView的常用属性

// superview:获得自己的父控件对象,每个子控件的superview只有一个,该属性只读
// 一般用于判断是否已经加入到视图中,避免重复添加
if (view.superview) return;

// subviews:获得自己的所有子控件对象,只读
NSLog(@"%@",view.subviews);

// 是否可与用户交互
view.userInteractionEnabled = YES;

// 裁剪超出范围的控件
view.clipsToBounds = YES;

// 设置透明度
view.alpha = 1;

// 设置背景颜色,默认是clearColor
view.backgroundColor = [UIColor colorWithRed:1 green:1 blue:0 alpha:0.3];//颜色由三原色组成
  • [UIView 属性分析大全][http://www.jianshu.com/p/4baf286f4bc1]

焦点光灯效果

本人这里封装了两种常见的种类。(圆形, 矩形, 圆角矩形, 自定义视图)

typedef NS_ENUM(NSInteger, YLZHoleType)
 {
     YLZHoleTypeCirle,
     YLZHoleTypeRect,
     YLZHoleTypeRoundedRect,
     YLZHoleTypeCustomRect
 };

接下来重写
- (void)drawRect:(CGRect)rect
透过项目剖断, 来重写对应的功效。

诸如, 圆形的代码:

if (hole.holeType == YLZHoleTypeRoundedRect) {
             YLZRoundedRectHole *rectHole = (YLZRoundedRectHole *)hole;
             CGRect holeRectIntersection = CGRectIntersection( rectHole.holeRect, self.frame);
             UIBezierPath *bezierPath = [UIBezierPath bezierPathWithRoundedRect:holeRectIntersection
                                                                   cornerRadius:rectHole.holeCornerRadius];

             CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), [[UIColor clearColor] CGColor]);
             CGContextAddPath(UIGraphicsGetCurrentContext(), bezierPath.CGPath);
             CGContextSetBlendMode(UIGraphicsGetCurrentContext(), kCGBlendModeClear);
             CGContextFillPath(UIGraphicsGetCurrentContext());

}

UIView的常用方法

// 添加一个子控件view
// 底层实现:
    // 1.判断传入view之前有没有父控件
    // 2.如果有,先移除之前父控件
    // 3.再重新添加view
[self.view addSubview:view];

// 从父控件中移除
// 多用于动画控件完成动画后移除
[self.view removeFromSuperview];

事件穿透

在那在此以前, 提一个主意。

- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event;
// recursively calls -pointInside:withEvent:. point is in the receiver’s coordinate system

借使完结了最顶层的 UIView 的 hitTest 方法,在有些景况重返下层的有些按键实例,即一定于把特别按钮的事件透出来了,比方在点击落在该开关上时,不管那些按键在 UIView 下多少层都足以把它挖出来。

就此, 重写hitTest方法就能够。

 #pragma mark - UIView Overrides
  - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event
 {
     UIView *hitView = [super hitTest:point withEvent:event];

     if (hitView == self)
     {
         for (UIView *focus in self.focusView) {
             if (CGRectContainsPoint(focus.frame, point))
             {
                 return focus;
             }
         }
     }

     return hitView;
 }

自定义遮罩视图(指导, 功效表明)源码 分析iOS开荒- 自定义遮罩视图(辅导, 成效表达)源码 分析 我们一向利用App的时候, 平常在第一...

UIView获取子控件或其标志

  • 支出中,平时须求选择到标志来获得控件或其标记,用于控件间的联动,那时的可选方案如下:

    • 方案一:通过tag值(不推荐使用)

      1. tag:控件的ID(标志),父控件可以透过tag来找到呼应的子控件;
      2. tag私下认可值是0
      3. tag值是不管三七二十一安装,很轻易混乱,能不用就毫无,开辟时间越长,tag值管理会更加的劳顿

        // 设置tag值
        view.tag = 10;
        
        // 根据tag获取view,没找到返回nil
        UIView *view = [self.view viewWithTag:10];
        
        // viewWithTag:内部的大致实现思路
        - (UIView *)viewWithTag:(NSInteger)tag
        {
            if (self.tag == tag) return self;
        
            for (UIView *subview in self.subviews) {
            // 返回tag标识找出对应的控件(一般都是子控件)
                return [subview viewWithTag:tag];
            }
        }
        
    • 方案二:通过subviews数组获取(不推荐)

      • 相差:一般都是通过调控器的view的subviews获取子控件或其标志,但该view不仅独有大家团结创制的控件,也是有系统创制的控件,不佳把握,轻松出错;其余子成分种类一般不等同,不方便管理

        // 获取某个子控件
        UIView *view = self.view.subviews[0];
        
        // 获取控件标识
        NSInteger index = [self.views indexOfObject:view]
        
    • 方案三:新建数组,获取角标获取控件(推荐方案)

      // 新建控件数组并作为成员属性
      NSMutableArray *views = [NSMutableArray array];
      self.views = views;
      
      // 将控件添加到控件数组中
      [self.views addObject:view];
      [self.views addObject:view1];
      [self.views addObject:view2];
      
      // 获取控件标识
      NSInteger index = [self.views indexOfObject:view]
      
      // 获取控件
      UIView *view = self.views[index];
      

UIViewContentMode属性(只对UIImageView控件中图纸有效)

  • 带有scale单词的:图片有极大恐怕会拉伸(常用)
scale的属性 拉伸方式(不旋转图片 保持原宽高比? 备注
UIViewContentModeScaleToFill 不按比例拉伸拉伸图片至填充整个控件 图片跟控件同宽同高默认属性
UIViewContentModeScaleAspectFill 按比例拉伸图片,直至图片拉伸至填充整个控件 图片一边与控件相等,另一边于控件
UIViewContentModeScaleAspectFit 按比例拉伸图片,直至图片任一边与控件对应边一样大 图片一边与控件相等,另一边于控件
  • 没有scale单词的:图片相对不会被拉伸
不含scale的属性 是否拉伸 在控件中的显示位置
UIViewContentModeCenter 保持图片的原尺寸 居中
UIViewContentModeTop 同上 上面
UIViewContentModeBottom 同上 下面
UIViewContentModeLeft 同上 左面
UIViewContentModeRight 同上 右面
UIViewContentModeTopLeft 同上 左上
UIViewContentModeTopRight 同上 右上
UIViewContentModeBottomLeft 同上 左下
UIViewContentModeBottomRight 同上 右下

UIView的包裹(自定义控件)

  1. 自定义控件基础:老爹和儿子控件
    1. 各类控件都以个容器,能包容别的控件
    2. 个中型Mini控件是大控件的子控件
    3. 大控件是内部小控件的父控件
  • 自定义控件的含义
    1. 就算一个view内部的子控件相当多,一般会挂念自定义叁个view,把它其中子控件的创办屏蔽起来,不暴光给外部

      2. 外界可以传入对应的模型数据给view,view拿到模型数据后给内部的子控件设置对应的数据
    
  • 步骤:

    1. 新建八个传承UIView的类
    • 始建并布局子控件(纯代码或然xib格局)

      1. 纯代码格局

        1. 提供类格局及对象方法
        2. 能够重写init或initWithFrame方法
          2. 重写init创造时,底层也会调用initWithFrame方法,先调用父类的,再调用子类自个儿;
        3. 在initWithFrame:方法中增多并开首化子控件
        4. 在layoutSubviews方法中布局子控件,布局前务必调用[super layoutSubviews];
      2. xib方式

        1. 新建一个xib文件(xib的文本名最棒跟控件类名同样)
        2. 增多子控件、设置子控件属性
        3. 4887王中王鉄算盘奖结果,修改最大的父控件的class为控件类名
        4. 将子控件进行连线
        5. 提供多少个类措施及对象方法用于加载xib
          1. 开始化时底层不调用initWithFrame:方法,而是调用`initWithCoder:方法。加载时:
        6. initWithCoder是率先个实践的艺术
        7. awakeAfterUsingCoder:是第三个实施的方法
        8. awakeFromNib是最终实行的点子,可在此处开始化控件
    • 提供模型属性,重写模型属性的set方法
      • 在set方法中给子控件设置数据

渐变式动画

  1. 头尾式

    // 开始动画
    [UIView beginAnimations:nil context:nil];
    
    // 设置动画属性
    // 设置动画时间
    [UIView setAnimationDuration:2.0];
    
    /* 需要执行动画的代码 */
    
    // 提交动画
    [UIView commitAnimations];
    
  • block式(实质是苹果对头尾式动画的包装)

    // 在1秒内执行动画
    [UIView animateWithDuration:1.0 animations:^{
        // 显示指示版
        self.hudLbl.alpha = 1;//不透明
    
    } completion:^(BOOL finished) {
    
        // 上一个动画完毕后延迟3秒后,在1秒内执行下列动画
        [UIView animateWithDuration:1.0 delay:3.0 options:kNilOptions animations:^{
    
            // 隐藏指示版
            self.hudLbl.alpha = 0;//完全透明
        } completion:nil];
    }];
    

UIView界面跳转

  1. 若果2个控件如若有臃肿部分,那么处于地方的那贰个控件会盖住上面包车型客车。下边包车型大巴控件不是晶莹剔透或颜色为clearColor,会掩盖上面控件的显得

  2. 支出中需求在一些时刻将钦定的控件呈现出来,即放在最下面;这时急需分界面跳转。

  • 分界面跳转重要的主意有3种:

    1. 用 UINavigationController 把 View B push 进来。

      [self.navigationController pushViewController:nextView animated:YES];
      
    • 用Modal的 presentModalViewController 方法把 View B 盖在地方。

      [self presentModalViewController:nextView animated:YES];
      
    • 调度窗口subView相月素的逐个,也可选拔于方今突显的父控件中,让其某块头控件呈现出来;

      // AppDelegate.m 类
      // 添加到窗口上
      [self.window addSubview:viewB];
      [self.window addSubview:viewA];
      
      // 调整控件位置顺序,让其显示出来
      [self.window bringSubviewToFront:viewB];
      
  • 分界面跳转的本质重新调节窗口subViews中元素的次第,让最后多个因素呈现

UIView圆角裁剪和加边框格局

方案一 :最简便易行的

// 最简单的方式就是设置每个 View 自带的 layer 的属性即可
view.layer.cornerRadius = 8.0f;

// 如果该 View 有子 View,会是这种状况

4887王中王鉄算盘奖结果 3

view中有view

// 如果需要切掉除了保留部分以外的子 View,那么需要加上
view.clipsToBounds = YES;

4887王中王鉄算盘奖结果 4

InMrEb2.jpg-web.jpg

// 如果需要边框,也简单,加上 layer 的边框设置就可以。
view.layer.borderWidth=10.0f;
view.layer.borderColor= [UIColor yellowColor].CGColor;

// 但是如果细看,会发现边框有严重的黑边,特别是当 View 背景色比较深的时候

4887王中王鉄算盘奖结果 5

轻易扩充边框的功能图

方案一总计:
1.1 好处:轻易,哪个地方需求圆角就在哪里设置就足以了。
1.2 劣势:这种办法管理的圆角很模糊,特别是在视图相当小的时候,品质不高,当背景观相比较深的时候有边框有黑边现象,并且一旦利用了 clipToBounds ,则会触发离屏渲染(这几个多少个十分大的坑,有意思味的话能够详细询问下),造成异常惨痛的卡顿难点,特别是在 UITableViewCell 的子 View 中如此使用,掉帧会很要紧。

4887王中王鉄算盘奖结果 6

简短裁剪的bug

方案二 :设置 mask layer

// 使用贝赛尔曲线,并根据其路径,得到一个「遮罩 layer」,将其设置为 View 自带 layer 的 mask,盖掉其他部分,剩余中间的部分。也可以达到圆角效果。

- (void)setRoundedCorners {
   UIBezierPath* maskPath = [UIBezierPathbezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:size];

   CAShapeLayer* maskLayer = [CAShapeLayerlayer];
maskLayer.frame = self.bounds;
maskLayer.path = maskPath.CGPath;

   self.layer.mask = maskLayer;
}
   // 将这个 masklayer 加到自带 layer 上而不是设置为其 mask,代码如下:
   /*
   UIBezierPath* maskPath = [UIBezierPath bezierPathWithRoundedRect:v.boundsbyRoundingCorners:UIRectCornerAllCornerscornerRadii:CGSizeMake(halfW, halfW)];

   CAShapeLayer* maskLayer = [CAShapeLayer layer];
   maskLayer.frame = view.bounds;
   maskLayer.fillColor = [UIColor whiteColor].CGColor;
   maskLayer.path = maskPath.CGPath;
   maskLayer.backgroundColor= [UIColor greenColor].CGColor;

   [view.layer addSublayer:maskLayer];
   */

   // 效果图:
   // 一个只有四个角的 layer 盖到原来的 layer 上,达到圆角效果。
   // 因为是盖住四角达到的效果,所以不用设置 maskToBounds 也可以去掉子 View 超出中心圆的部分,但是同样会触发离屏渲染。用的时候应当小心。
```![z26jAbI.jpg-web.jpg](http://upload-images.jianshu.io/upload_images/1566132-4b043dfcc9ad6d73.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240)

![设置mask layer圆角效果图](http://upload-images.jianshu.io/upload_images/1566132-3da6e98933c5125d.jpg?imageMogr2/auto-orient/strip|imageView2/2/w/1240)


```objc
   // 如果要加上边框,可以在 self.layer 上加一个圆环 layer 达到边框效果,代码如下:

- (void)setRoundedCorners:(UIRectCorner)corners borderWidth:(CGFloat)borderWidth borderColor:(UIColor*)borderColor cornerSize:(CGSize)size {
   UIBezierPath* maskPath = [UIBezierPathbezierPathWithRoundedRect:self.bounds byRoundingCorners:corners cornerRadii:size];

   CAShapeLayer* maskLayer = [CAShapeLayerlayer];
   maskLayer.fillColor = [UIColorblueColor].CGColor;
   maskLayer.frame = self.bounds;
   maskLayer.path = maskPath.CGPath;

   self.layer.mask = maskLayer;

   if(borderWidth >0) {
       CAShapeLayer*borderLayer = [CAShapeLayerlayer];

       // 用贝赛尔曲线画线,path 其实是在线的中间,这样会被      layer.mask(遮罩层)遮住一半,故在 halfWidth 处新建 path,刚好产生一个内描边
       CGFloathalfWidth = borderWidth /2.0f;
       CGRectf =CGRectMake(halfWidth,  halfWidth,CGRectGetWidth(self.bounds) - borderWidth,CGRectGetHeight(self.bounds) - borderWidth);

       borderLayer.path = [UIBezierPathbezierPathWithRoundedRect:f byRoundingCorners:corners cornerRadii:size].CGPath;
       borderLayer.fillColor = [UIColorclearColor].CGColor;
       borderLayer.strokeColor = borderColor.CGColor;
       borderLayer.lineWidth = borderWidth;
       borderLayer.frame =         CGRectMake(0,0,CGRectGetWidth(f),CGRectGetHeight(f));
       [self.layer addSublayer:borderLayer];
}

    // 我们会看到这里 borderLayer 的坐标很奇怪,这里解释一下,如果我们这样设置坐标,用以下方式添加 borderLayer,会是什么效果呢?

    UIBezierPath* borderPath= [UIBezierPathbezierPathWithRect:v.bounds];

    CAShapeLayer* borderLayer= [CAShapeLayer layer];
    borderLayer.path =borderPath.CGPath;
    borderLayer.fillColor = [UIColor clearColor].CGColor;
    borderLayer.strokeColor =   [[UIColorblackColor]colorWithAlphaComponent:0.5].CGColor;
    borderLayer.lineWidth =10;
    borderLayer.frame = v.bounds;
    [v.layer addSublayer:borderLayer];

    // 效果图:
    // 效果分析:
    // 边框刚好骑在了边界上,如果这时候我们在使用 maskLayer 做圆角,那骑在边界上的边框有外面一半将被吃掉,只剩下一半,所以要把 borderLayer 往里挪一半的边框的距离,避免让 maskLayer 吃掉外面那部分边框。

4887王中王鉄算盘奖结果 7

设置mask layer扩大边框效果图1

放大后的效果图:

4887王中王鉄算盘奖结果 8

安装mask layer增添边框效果图2

方案二总结:
1.1 好处:简单,并且可以使用贝赛尔曲线达到任意形状的「剪切」,也可以根据情况选择要「切」的角,比如如果只需要切右上和右下两个角,那么只需要改一下贝赛尔曲线

   UIBezierPath* maskPath = [UIBezierPathbezierPathWithRoundedRect:view.bounds byRoundingCorners:UIRectCornerTopRight|UIRectCornerBottomRightcornerRadii:CGSizeMake(halfW, halfW)];

1.2 缺点:上一种方式的缺点,这个方式也都有

方案三 :生成圆角背景图片格局

   CGFloat w = 200; 
   UIView *v = [[UIView alloc] initWithFrame:CGRectMake(0,0, w, w)]; 
   v.backgroundColor= [UIColor redColor];
   [self.view addSubview:v];
   v.center = self.view.center; 

   UIImage *img = [UIImage imageWithColor:v.backgroundColorandSize:v.bounds.size]; 
   img = [img roundedWithBorderWidth:10borderColor:[UIColorgreenColor]]; 

   UIImageView *imageView = [[UIImageView alloc] initWithImage:img];
   [v insertSubview:imageViewatIndex:0]; 
   v.backgroundColor= [UIColor clearColor];

   // 根据 View 的大小和背景色,生成一张图片,生成图片的方法如下:
   @implementationUIImage(ZQExtension) 
     (UIImage*)imageWithColor:(UIColor*)color andSize:(CGSize)size { 
   CGRectrect =CGRectMake(0.0f,0.0f, size.width, size.height); 

   UIGraphicsBeginImageContextWithOptions(rect.size,NO, [UIScreenmainScreen].scale); 

   CGContextRefcontext =UIGraphicsGetCurrentContext();

   CGContextSetFillColorWithColor(context, [colorCGColor]);

   CGContextFillRect(context, rect); 

   UIImage*image =UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); returnimage; 
   } 
   @end

   // 再将这张图片圆角处理,并加上边框,最后创建一个 UIImageView,设置该 imageView 的 image 为圆角处理后的图片,并插入 View 的最底层,造成一种圆角处理的假象。

   // 圆角处理的代码如下:

@implementationUIImage(ZQExtension)

- (UIImage*)roundedWithBorderWidth:(CGFloat)borderWidth borderColor:(UIColor*)borderColor {

   CGFloatinset =1;
   CGFloatwidth =self.size.width;
   CGFloatheight =self.size.height;
   CGFloatcornerRadius;
   UIBezierPath*maskShape;

   if(width > height) {
        cornerRadius = height / 2.0- inset;
       maskShape = [UIBezierPathbezierPathWithRoundedRect:CGRectMake((width-height)/2.0  inset,0  inset, height-2*inset, height-2*inset) cornerRadius:cornerRadius];

   }else{
       cornerRadius = width / 2.0- inset;
       maskShape = [UIBezierPathbezierPathWithRoundedRect:CGRectMake(0 inset, (height-width)/2.0 inset, width-2*inset, width-2*inset) cornerRadius:cornerRadius];
}

   UIGraphicsBeginImageContextWithOptions(self.size,NO, [UIScreenmainScreen].scale); 

   CGContextRefctx =UIGraphicsGetCurrentContext(); 

   CGContextSaveGState(ctx); 

   CGContextAddPath(ctx, maskShape.CGPath);

   CGContextClip(ctx); CGContextTranslateCTM(ctx,0, height); 

   CGContextScaleCTM(ctx,1.0,-1.0); 

   CGContextDrawImage(ctx,CGRectMake(0,0, width, height),self.CGImage); 

   CGContextRestoreGState(ctx);

   if(borderWidth >0) { 
       [borderColor setStroke]; 

       CGFloathalfWidth = borderWidth /2.0; 

       UIBezierPath*border = [UIBezierPathbezierPathWithOvalInRect:CGRectMake(halfWidth, halfWidth,self.size.width - borderWidth ,self.size.width - borderWidth)];

       CGContextSetShouldAntialias(ctx,YES);

       CGContextSetAllowsAntialiasing(ctx,YES);

       CGContextSetLineWidth(ctx, borderWidth);

       CGContextAddPath(ctx, border.CGPath);

       CGContextStrokePath(ctx); 
   } 
       UIImage*resultingImage =UIGraphicsGetImageFromCurrentImageContext();

       UIGraphicsEndImageContext(); 

       returnresultingImage; 

   } @end

   // 效果如下:
   // 这里使用贝赛尔曲线加边框的坐标和第二种方式的类似。

4887王中王鉄算盘奖结果 9

生成圆角背景图片格局效果图

方案三 :生成圆角背景图片方式总结
3.1 好处:是不会触发离屏渲染,生成图片和圆角都由 CPU 处理,且边框清晰,没有黑边,
3.2 缺点:不能处理子 View,在四角处的子 View 不会被「切」掉,毕竟是一个圆角背景造成的圆角假象,对子 View 没有什么影响力。

方案四 :使用遮罩层(推荐)

    // 这个遮罩层不是上面提遮罩 layer,是一张你想要保留的形状的一张图片,比如想要圆角图片,可以让设计师做一张这样图片:

    // 中间透明,周围是想要盖住的形状,最中看到的是中间留下来的形状,这种做法没有什么性能损耗,就是需要麻烦设计师做一张图。

    // 具体的做法见,也可以跑一下这个 demo 看看怎么对 UITableView 优化的。 https://github.com/johnil/VVeboTableViewDemo/blob/master/VVeboTableViewDemo/view/VVeboTableViewCell.m#L46

    // 设计师需要做的图:

4887王中王鉄算盘奖结果 10

遮罩层图片

五、总括:UIView圆角裁剪和加边框格局汇总

5.1 在[离屏渲染优化][http://www.jianshu.com/p/ca51c9d3575b] (建议好好看看篇文章)中,seedante 对各种圆角方案也有总结对比,最终得到这样一个结果:

5.2 任何时候优先考虑避免触发离屏渲染,无法避免时优化方案有两种:

5.3 Rasterization:适用于静态内容的视图,也就是内部结构和内容不发生变化的视图,对上面的所有效果而言,在实现成本以及性能上最均衡的。即使是动态变化的视图,开启 Rasterization 后能够有效降低 GPU 的负荷,不过在动态视图里是否启用还是看 Instruments 的数据。

5.4 规避离屏渲染,用其他手法来模拟效果,混合图层是个性能最好、耗能最少的通用优化方案,尤其对于 rounded corer 和 mask。

5.5 总的来说,圆角方案需要根据情况具体选择用哪种方式。

UIView的layout机制解释

// ios layout机制相关方法

- (CGSize)sizeThatFits:(CGSize)size
- (void)sizeToFit
——————-

- (void)layoutSubviews
- (void)layoutIfNeeded
- (void)setNeedsLayout
——————–

- (void)setNeedsDisplay
- (void)drawRect

/*
layoutSubviews在以下情况下会被调用:

1、init初始化不会触发layoutSubviews

   但是是用initWithFrame 进行初始化时,当rect的值不为CGRectZero时,也会触发

2、addSubview会触发layoutSubviews

3、设置view的Frame会触发layoutSubviews,当然前提是frame的值设置前后发生了变化

4、滚动一个UIScrollView会触发layoutSubviews

5、旋转Screen会触发父UIView上的layoutSubviews事件

6、改变一个UIView大小的时候也会触发父UIView上的layoutSubviews事件

在苹果的官方文档中强调:

      You should override this method only if the autoresizing behaviors of the subviews do not offer the behavior you want.

layoutSubviews, 当我们在某个类的内部调整子视图位置时,需要调用。

反过来的意思就是说:如果你想要在外部设置subviews的位置,就不要重写。

刷新子对象布局

-layoutSubviews方法:这个方法,默认没有做任何事情,需要子类进行重写
-setNeedsLayout方法: 标记为需要重新布局,异步调用layoutIfNeeded刷新布局,不立即刷新,但layoutSubviews一定会被调用
-layoutIfNeeded方法:如果,有需要刷新的标记,立即调用layoutSubviews进行布局(如果没有标记,不会调用layoutSubviews)

如果要立即刷新,要先调用[view setNeedsLayout],把标记设为需要布局,然后马上调用[view layoutIfNeeded],实现布局

在视图第一次显示之前,标记总是“需要刷新”的,可以直接调用[view layoutIfNeeded]

重绘

-drawRect:(CGRect)rect方法:重写此方法,执行重绘任务
-setNeedsDisplay方法:标记为需要重绘,异步调用drawRect
-setNeedsDisplayInRect:(CGRect)invalidRect方法:标记为需要局部重绘

sizeToFit会自动调用sizeThatFits方法;

sizeToFit不应该在子类中被重写,应该重写sizeThatFits

sizeThatFits传入的参数是receiver当前的size,返回一个适合的size

sizeToFit可以被手动直接调用

sizeToFit和sizeThatFits方法都没有递归,对subviews也不负责,只负责自己

———————————-

layoutSubviews对subviews重新布局

layoutSubviews方法调用先于drawRect

setNeedsLayout在receiver标上一个需要被重新布局的标记,在系统runloop的下一个周期自动调用layoutSubviews

layoutIfNeeded方法如其名,UIKit会判断该receiver是否需要layout.根据Apple官方文档,layoutIfNeeded方法应该是这样的

layoutIfNeeded遍历的不是superview链,应该是subviews链

drawRect是对receiver的重绘,能获得context

setNeedDisplay在receiver标上一个需要被重新绘图的标记,在下一个draw周期自动重绘,iphone device的刷新频率是60hz,也就是1/60秒后重绘 
*/

本文由4887王中王鉄算盘奖结果发布于操作系统,转载请注明出处:自定义遮罩视图,iosuiview对象实现

关键词:

上一篇:没有了

下一篇:没有了

最火资讯