iOS 项目小知识点

自定义控件

在- (instancetype)initWithFrame:(CGRect)frame
里自定义控件
在- (void)layoutSubviews 里布局控件

名字叫attributes并且是NSDictionary *类型的参数,它的key一般都有以下规律
 1.iOS7开始
 1> 所有的key都来源于: NSAttributedString.h
 2> 格式基本都是:NS***AttributeName 
 2.iOS7之前
 1> 所有的key都来源于: UIStringDrawing.h
 2> 格式基本都是:UITextAttribute***
   NSString * title = [button currentTitle]; // 获取按钮当前title文字
   CGSize size = [title sizeWithAttributes:@{NSFontAttributeName:button.titleLabel.font}];//根据字体计算大小
 _titleUnderLine.backgroundColor = [titleButton titleColorForState:UIControlStateSelected]; //根据按钮title 颜色设置下划线颜色

cell分割线顶头操作

1.第一种方法,利用系统的属性去设置

项目中经常会有些需求是cell 的分割线顶头,而系统默认的是前面有一定的间距,其实这是由一个separatorInset 属性造成的(iOS7以后出的),通过打印:

- (void)viewDidAppear:(BOOL)animated
{
    NXLog(@"margin:%@",NSStringFromUIEdgeInsets(self.tableView.separatorInset));
}

打印结果:
margin:{0, 15, 0, 0}   //可以看出来系统默认有一个15的偏移量

将该属性设置为空,现在tableView里面设置:

 self.tableView.separatorInset = UIEdgeInsetsZero; //分隔线顶头,这个只是设置tableView 顶头,还需要在cell里设置cell的顶头
通过这一步设置发现分割线比之前左移了不少,但是还没顶到头,这里就判断是cell自身的原因

同时在cell 里面进行设置:

self.separatorInset = UIEdgeInsetsZero;//cell分割线顶头

这时候运行再看结果,已经OK顶到头。

Paste_Image.png

tableView底层实现

1.tableView 先把所有cell 的尺寸计算好,保存到一个数组里。
2.当cell 要显示的时候就拿到这个cell 去设置frame : cell.frame = frames[row]。

2.第二种方法,重写cell 的setFrame,可以随意设置cell 的间距,比较万能。

主要思路:主要就是利用了上面的tableView 的实现原理。
1.首先设置tableView的背景色为分割线的颜色。
2.禁掉系统的分割线
3.在cell类中重写setFrame 方法。

 self.tableView.backgroundColor = NXColor(220, 220, 221);
 self.tableView.separatorStyle = UITableViewCellSeparatorStyleNone;

//在自定义的cell类中:
- (void)setFrame:(CGRect)frame
{
    NXLog(@"cellFrame:%@",NSStringFromCGRect(frame));
    //因为之前的frame 都是计算好的,这是在要显示的时候去设置一下,所以这里只去将高度减1,不会影响到其他尺寸
    frame.size.height -=1;  // 如果想要间距更大的话可以 -=10 等,随意这种方法比较万能
    //这个才是系统真正的设置frame。重写了这个方法会将系统的frame清空,所以需要调用这个
    [super setFrame:frame];
}

屏幕适配

1.一个view从Xib 加载,需不需要重新固定一下尺寸? 这个是一定得需要的,比较保险。
2.一般添加xib view的时候是在viewDidLoad 里面加载,但是设置尺寸frame 最好在viewDidLayoutSubViews 里面去设置。因为viewDidLoad 并不会去布局frame ,只是拿到xib 设置的大小属性,只有在 viewDidLayoutSubViews 里才会去根据布局去确定控件的最终尺寸(不过添加view 的操作不能放在这里,因为这个方法会调用多次)

🌰:
- (void)viewDidLoad {
    [super viewDidLoad];
    UIView * loginView = [NXLoginRegistView loginView];
    [self.middleView addSubview:loginView];
    UIView * registView = [NXLoginRegistView registView];
    [self.middleView addSubview:registView];
    UIView * fastLogin = [NXFastLoginView fastLoginView];
    [self.bottomView addSubview:fastLogin];
}

- (void)viewDidLayoutSubviews
{
    [super viewDidLayoutSubviews];
    //在这里面设置尺寸
    UIView * loginView = [self.middleView subviews][0];
    loginView.frame = CGRectMake(0, 0, self.middleView.frame.size.width * 0.5, self.middleView.frame.size.height);
    UIView * registView = [self.middleView subviews][1];
    registView.frame = CGRectMake(self.middleView.frame.size.width * 0.5, 0, self.middleView.frame.size.width * 0.5, self.middleView.frame.size.height);
    UIView * fastLogin = [self.bottomView subviews][0];
    fastLogin.frame = CGRectMake(0, 0, self.bottomView.frame.size.width, self.bottomView.frame.size.height);
}


从Xib加载的view 设置动画

是通过操作约束去设置,比如要修改x值产生动画就找到x的约束拖线添到类文件中成为属性,然后对该属性进行操作,在设置动画时,必须加上这句 :

        [self.view layoutIfNeeded]; //这句话很重要,只有重新布局才会产生动画效果,否则没有动画效果

例如

[UIView animateWithDuration:1 animations:^{
        self.testViewWidthConstraint.constant = 200;
        [self.view layoutIfNeeded]; //这句话很重要,只有重新布局才会产生动画效果,否则没有动画效果
}];

一个xib文件可以管理多个view

重写button 的布局

这个在项目中也很常见,默认的按钮布局是左右布局,即左边imageView + 右边label 。而好多项目中需要的是上下布局,所以需要自定义button,重新布局。这里在layoutSubViews 方法里面进行,因为在这个方法里面所有的控件尺寸都已经是计算好的,我们就不需要重新去计算,只要改一下布局就可以。

🌰:
- (void)layoutSubviews
{
    [super layoutSubviews];
    self.imageView.nx_y = 0;
    self.imageView.nx_centerX = self.nx_centerX;
    self.titleLabel.nx_y = self.nx_height - self.titleLabel.nx_height;
    self.titleLabel.nx_centerX = self.nx_centerX;
}

自定义tabBar的问题

项目中需要定制tabBar 的时候需要用到自定义tabBar,这里需要注意的是不能直接通过如下的赋值语法进行替换:

🌰:
   NXTabBar * nTabBar = [[NXTabBar alloc]init];
   self.tabBar = nTabBar;
这样替换的话是没有效果的,需要用KVC 的形式进行替换:
   
 [self setValue:nTabBar forKey:@"tabBar"];

iOS类私有属性的访问和修改

http://www.jianshu.com/p/a667f0ce9573

iOS 类别中添加属性(属性和实例变量的区别)

【转】iOS中属性与成员变量的区别
iOS Category中添加属性和成员变量的区别

通过category 和 runtime 设置textField 的 placehold 的颜色

项目中经常会用到textField ,比如登录注册等。默认的光标是蓝色,而且placeholder的文字颜色也是灰的,不会变化。有时候项目会需求点击后光标为白色,同时placeholder 的文字高亮为白色(或其他颜色)。通过头文件一级一级往上查找,找到了一个如下属性:

@property(nullable, nonatomic,copy)   NSAttributedString     *attributedPlaceholder

利用这个属性,用如下方法进行颜色修改:

 //第一个参数: 需要被设置属性的字符串
 //第二个参数: 需要设置的属性
    self.attributedPlaceholder = [[NSAttributedString alloc]initWithString:self.placeholder attributes:@{NSForegroundColorAttributeName:[UIColor whiteColor]}];

这个方法虽然达到效果了,但是比较麻烦,通过如下图的方式,随便写一个textField ,然后打上短点,我们可以看到在textField 内部有placeholderLabel的属性,所以可以尝试通过对该属性进行操作,直接修改颜色。不过通过下面的调试框可以看到这是一个私有属性。想要对私有属性进行访问和修改可以使用kvc 或者 runtime ,这里使用kvc ,新建一个类别,添加属性placeholderColor,在set 方法里面去进行设置:

🌰:
h 文件
#import <UIKit/UIKit.h>

@interface UITextField (NXTextField)
@property (nonatomic ,strong)UIColor *placeholdColor;
@end

m 文件
- (void)setPlaceholdColor:(UIColor *)placeholdColor
{
    //根据判断,textField 内部的 placehold  是一个label 的标签
    
    UILabel * placeholderLabel = [self valueForKey:@"placeholderLabel"];
    placeholderLabel.textColor = placeholdColor;
    
}
- (UIColor *)placeholdColor{
    return nil;  // 暂时先不用,返回一个nil ,去掉系统的警告⚠️
}

然后可以在需要设置的地方引入category 的头文件,通过如下方式即可设置颜色:

    self.placeholdColor = [UIColor lightGrayColor];
Paste_Image.png

通过上面category 的方式基本可以满足需求,但是如果textField 如下设置的时候就会出现无效:

// 这样设置是可以的:
  self.placeholder = @"44444";
  self.placeholdColor = [UIColor lightGrayColor];

//将上面的换个顺序执行,就会无效:
 self.placeholdColor = [UIColor lightGrayColor];
 self.placeholder = @"44444";

//这是因为之前没有设置placeholder文字,设置颜色的时候通过断点可以看到,此时获取不到placeholderLabel,所以导致设置颜色无效。

解决这个问题就需要用到runtime
1.先将 placeholderColor 保存起来
2.在设置placeholder 的时候再取出来设置颜色

- (void)setPlaceholdColor:(UIColor *)placeholdColor
{
    //先将placeholdColor 存起来
    //参数一:给哪个对象添加成员属性
    //参数二:成员属性的名字
    //参数三:成员属性的值
    objc_setAssociatedObject(self, @"placeholdColor", placeholdColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    
    //根据判断,textField 内部的 placehold  是一个label 的标签
    
    UILabel * placeholderLabel = [self valueForKey:@"placeholderLabel"];
    placeholderLabel.textColor = placeholdColor;
    
}
- (UIColor *)placeholdColor{
    return objc_getAssociatedObject(self, @"placeholdColor");
}

//自顶一个setPlaceholder方法,在内部1.先设置文字。 2.设置颜色
- (void)setNX_Placeholder:(NSString *)placeholder
{
    self.placeholder = placeholder;
    self.placeholdColor = self.placeholdColor;
}

上面方法有个缺点就是每次设置placeholder文字还得去调用

    [self setNX_Placeholder:@"hhhh"];

所以想要直接通过: ** self.placeholder = @"hhhh"; 去设置,就需要用到方法交换。因为交换只需要做一次操作,所以,放到+(void)load**方法里去操作,在category文件中加入:

+ (void)load{
    Method setPlaceholder = class_getInstanceMethod(self, @selector(setPlaceholder:));
    Method setNx_Placeholder = class_getInstanceMethod(self, @selector(setNX_Placeholder:));
    method_exchangeImplementations(setPlaceholder, setNx_Placeholder);
}

计算总行数的万能公式

row = (count -1 ) / column +1;


#tableView  调整cell 间距
>用storyBoard 拖出来的cell 默认显示的是如下图的间距(因为要使用到静态单元格,所以用到storyBoard,一般情况下用xib最好)
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-1b72dbea6ce3c123.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

这里用的是分组style,所以可以通过设置  

self.tableView.sectionFooterHeight = 0;
self.tableView.sectionHeaderHeight = 0;

得到如下效果:

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-13edc8b059e545ce.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以看到每个分组之间的间距已经没了,但是第一个cell 距离顶部还是有好大一部分距离,这里判断可能是tableView 的间距,通过下面打印

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-ae86d387b8e4f8e8.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

可以看到tableView 距离顶部确实有64 的偏移量,但是这个刚好是空出导航栏的高度,所以排除是tableView 的问题,接下来就判断是cell 的问题,通过下面打印

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-bc281de06bbc0996.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
可以看到分组内部第一个cell 的Y 值默认是35 ,那么这里可以在viewDidLoad 方法里进行如下调整就可以(这里需要间隔为10)

self.tableView.sectionFooterHeight = 0;
self.tableView.sectionHeaderHeight = 10;
self.tableView.contentInset = UIEdgeInsetsMake(-25, 0, 0, 0);

#跳转到网页
>1.`Safari  openURL`  自带很多功能:进度条,刷新,前进,倒退等功能。但是需要跳出到当前应用。
2.`UIWebView`  在当前应用打开网页,并且带有Safari(自己实现),不能实现进度条。
3.`SFSafariViewController`   专门用来显示网页,既可以在当前应用显示网页,又可以有Safari的功能。iOS9以后才可以用。
4.WKWebView
#判断view 是否被加载

随便一个都可以:
UIViewController * vc = self.childViewControllers[button.tag];
if (vc.isViewLoaded) {//如果已经被加载过就不重复加载(加载view 的时候会调用 viewDidLoad)
return ;
}
UIView * view = vc.view;
if (view.superview) { // 如果有父view ,说明已经被添加过
return;
}
if (view.window) { //如果有容器,说明已经被添加过
return;
}


#  一些全局常量,尽量使用extern来引用,其次再选择考虑Macro
>[iOS const的使用](http://www.cnblogs.com/oumygade/p/4316024.html)
[关于全局常量的定义](http://www.jianshu.com/p/ed2dfbca6e73)


#xib cell 拖线报错: Terminating app due to uncaught exception 'NSUnknownKeyException', reason: '[<NSObject 0x8a4b500> setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key 

>如下图,我已开始在File`s Owner里也设置了class是NXTopicCell,所以一直出错,这里吧他清空,设置下面的Topic Cell 的class如下就可以
![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-ab6ca235996cb993.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>xib拖的UILabel 没有自动换行:设置约束时不要给定高度就可以自动换行了。

#在heightForRowAtIndexPath获取cell 崩溃
http://www.jianshu.com/p/3da539540d21
>在网上看到有人是这么获取:

NXTopicCell * cell = (NXTopicCell *)[self tableView:tableView cellForRowAtIndexPath:indexPath];

试了下好像是可以。


>好像xib  里设置的高度是定死的,不会根据不同屏幕去重新计算高度。

##退出键盘

>1.监听scrollView实时滚动代理方法调用[self.view endEditing:YES]或者[self.textField resignFirstResponse];
2.touchBegin方法里,调用[self.view endEditing:YES]或者[self.textField resignFirstResponse];

##父View添加手势,子View不要去响应父View点击事件
[iOS 点击子视图不让其响应父视图手势](http://www.it610.com/article/4821007.htm)
>在做这样一个界面的时候

![Paste_Image.png](http://upload-images.jianshu.io/upload_images/972822-719483b12316733c.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

>背景的蒙版层是一个UIView,按钮的列表View添加到背景的蒙版View上。在蒙版层添加一个手势,点击dismiss掉这个界面和蒙版。但是遇到问题就是在点击按钮列表的View的时候也出发了单击手势。这个好像是系统默认的,参考事件响应链。为了避免这种效果,在手势触发事件中可以判断当前点击事件是否在父View上,如果是子View上则不去做操作,点击在父View上才去做dismiss的操作。代码如下:

  • (void)dismissMoreActionView:(UITapGestureRecognizer *)tap
    {
    //根绝tag获取子view
    UIView *subView = [self viewWithTag:100];

    /*
    BOOL contains = CGRectContainsPoint(CGRect rect, CGPoint point);
    判断一个CGRect是否包含再另一个CGRect里面,常用与测试给定的对象之间是否又重叠

    • (CGPoint)locationInView:(UIView *)view:
      该函数返回一个CGPoint类型的值,表示触摸在view这个视图上的位置,这里返回的位置是针对view的坐标
      */
      // 判断手势点击的位置是不是包含在子视图上
      if (!CGRectContainsPoint(subView.frame, [tap locationInView:self])) {
      [self removeFromSuperview];
      }
      }

----------------------
## 调整导航栏leftBarButtonItem 和 rightBarbuttonItem 的偏移量
[ 如何调整导航条上的leftBarButtonItem和rightBarButtonItem的位置](http://yikuwang.blog.51cto.com/6186082/1696156)

> ####重点:
这里需要提到BarButtonSystemItem的样式- UIBarButtonSystemItemFixedSpace
我不多说 请看官方解释:Blank space to add between other items. Only the [width](https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIBarButtonItem_Class/index.html#//apple_ref/occ/instp/UIBarButtonItem/width) property is used when this value is set.  反正我是看不懂 。再来说说negativeSpacer.width = 0 表示leftBarButtonItem的x离屏幕的左边还有15像素 同样表示rightBarButtonItem的CGRectGetMaxX()离屏幕的右边还有15像素 那么好像我们就可以通过negativeSpacer.width来调整leftBarButtonItem的位置。搞了老半天原来就是得靠这家伙。但有一点:negativeSpacer.width赋值负数对于leftBarButtonItem来说是左移多少像素 对于rightBarButtonItem来说是右移多少像素  反之亦然.

我这里想要设置右边的item 右移一点,因为默认的太偏左了:

UIButton * buyBtn = [[UIButton alloc]init];

 buyBtn.frame = CGRectMake(0, 0, 80, 40);
 
 buyBtn.backgroundColor = [UIColor orangeColor];
 
 [buyBtn setTitle:@"马上选购" forState:UIControlStateNormal];
 
 buyBtn.titleLabel.font = [UIFont systemFontOfSize:15];
      
 [buyBtn addTarget:self action:@selector(buyItemAction) forControlEvents:UIControlEventTouchUpInside];
 
 UIBarButtonItem * buyItem = [[UIBarButtonItem alloc]initWithCustomView:buyBtn];
 
 UIBarButtonItem * space = [[UIBarButtonItem alloc]initWithBarButtonSystemItem:UIBarButtonSystemItemFixedSpace target:nil action:nil];
 space.width = -10;
 
 self.navigationItem.rightBarButtonItems = @[space,buyItem];

##动态修改UIButton的title 的时候字体闪烁的问题:
 [UIButton-system类型 动态改变title 显示文字时闪动(解决方案)](http://blog.csdn.net/a330416020/article/details/41648787)
*******************
##assign 修饰代理导致野指针的问题
>在下面方法中调用了代理,崩溃指向这里显示野指针的错误,猜测是由于代理被释放而去调用产生的问题。点进去看到之前代理修饰用的是assign 修饰的,改为weak 后好像是可以了。猜测原因是由于assign 修饰的话 assign是指针赋值,不对引用计数操作,使用之后如果没有置为nil,可能就会产生野指针;而weak一旦不进行使用后,永远不会使用了,就不会产生野指针。

  • (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
    {
    if ([_delegate respondsToSelector:@selector(numberOfRowsInTableView:Insection:FromView:)])
    {
    NSInteger vRows = [_dataSource numberOfRowsInTableView:tableView Insection:section FromView:self];
    mRowCount = vRows;
    return vRows;
    }
    return 0;
    }
##单例问题
>在使用单例的时候,经常性就是直接写出下面的代码。不过最近在使用的时候会遇到单例导致崩溃的问题。一开始在写 `dispatch_once_t once_token`的时候没有用static 来修饰,后来加上static来修饰就好了。

  • (instancetype)shareInstance
    {
    static DrawTextView * drawText = nil;

    static dispatch_once_t once_token;

    dispatch_once(&once_token, ^{

      drawText = [[DrawTextView alloc]initWithFrame:CGRectMake(kDeviceW * 0.5- TextLayerWidth * 0.5, kDeviceH * 0.5 - TextLayerHeight * 0.5, TextLayerWidth, TextLayerHeight)];
      drawText.backgroundColor = [UIColor orangeColor];
    

    });
    return drawText;
    }


##在iPhone5s (10.2系统)拨打电话没有弹框提示,其他手机(10.3)有弹框

// 用这种方法的话会出现上面的问题
// NSString * phonNum = [NSString stringWithFormat:@"tel://%@",self.footerModel.dav_phone];

 NSString * phonNum = [NSString stringWithFormat:@"telprompt://%@", self.footerModel.dav_phone];

控制器切换从下往上,并且有导航栏:

可以用CATransition 动画,或者直接用presentModalViewController。
http://www.cocoachina.com/bbs/read.php?tid=7668

改变SearchBar 的取消按钮颜色,光标颜色

http://www.jianshu.com/p/66b5b777f5dc

DBL_EPSILON和 FLT_EPSILON的用法

DBL_EPSILON和 FLT_EPSILON主要用于单精度和双精度的比较当中:

image
image

比较方式

double b = sin(M_PI / 6.0);
if (fabs(((double)valueint)-value)<=DBL_EPSILON)
    (is int num);
else
    (is double num)

EPSILON是最小误差。如果整数值减去浮点数值误差低于DBL_EPSILON,则说明该数可以近似看成整数,否则则是浮点数……

绘制渐变的UIImage

首先网上找到的是第一种的方法,
1.先生成一个渐变图层

 CAGradientLayer * gradientLayer = [CAGradientLayer layer];
    CGFloat navigationBarHeight = statusBarRect.size.height +  self.navigationController.navigationBar.frame.size.height;
    gradientLayer.frame = CGRectMake(0, 400, [UIScreen mainScreen].bounds.size.width, navigationBarHeight);
    gradientLayer.colors = @[(id)[UIColor colorWithRed:22/255.0 green:112/255.0 blue:188/255.0 alpha:1].CGColor,(id)[UIColor colorWithRed:30/255.0 green:26/255.0 blue:150/255.0 alpha:1].CGColor];
    gradientLayer.startPoint = CGPointMake(0, 0.5);
    gradientLayer.endPoint = CGPointMake(1, 0.5);

2.通过渐变图层绘制图片:

- (UIImage *)imageFromLayer:(CALayer *)layer
{
    UIGraphicsBeginImageContextWithOptions(layer.frame.size, layer.opaque, [UIScreen mainScreen].scale);
    [layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage * outImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return outImage;
}

但是如上的方法好像并没有生成想要的图片。
网上找到第二种,可以生成想要的图片:

/**
 *  获取矩形的渐变色的UIImage(此函数还不够完善)
 *
 *  @param bounds       UIImage的bounds
 *  @param colors       渐变色数组,可以设置两种颜色
 *  @param gradientType 渐变的方式:0---&gt;从上到下   1---&gt;从左到右
 *
 *  @return 渐变色的UIImage
 */
- (UIImage*)gradientImageWithBounds:(CGRect)bounds andColors:(NSArray*)colors andGradientType:(int)gradientType{
    NSMutableArray *ar = [NSMutableArray array];
    for(UIColor *c in colors) {
        [ar addObject:(id)c.CGColor];
    }
    UIGraphicsBeginImageContextWithOptions(bounds.size, YES, 1);
    CGContextRef context = UIGraphicsGetCurrentContext();
    CGContextSaveGState(context);
    CGColorSpaceRef colorSpace = CGColorGetColorSpace([[colors lastObject] CGColor]);
    CGGradientRef gradient = CGGradientCreateWithColors(colorSpace, (CFArrayRef)ar, NULL);
    CGPoint start;
    CGPoint end;
    
    switch (gradientType) {
        case 0:
            start = CGPointMake(0.0, 0.0);
            end = CGPointMake(0.0, bounds.size.height);
            break;
        case 1:
            start = CGPointMake(0.0, 0.0);
            end = CGPointMake(bounds.size.width, 0.0);
            break;
    }
    // 线性渐变
    CGContextDrawLinearGradient(context, gradient, start, end, kCGGradientDrawsBeforeStartLocation | kCGGradientDrawsAfterEndLocation);
    UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
    CGGradientRelease(gradient);
    CGContextRestoreGState(context);
    CGColorSpaceRelease(colorSpace);
    UIGraphicsEndImageContext();
    return image;
}

还有一些其他类似方法,参考如下:iOS实现颜色渐变

画线控件(支持xib和storyboard)

自定义一个控件,继承自UIView,.h文件声明属性

#import <UIKit/UIKit.h>

NS_ASSUME_NONNULL_BEGIN

@interface ADHouseSourceDottedLine : UIView
@property (nonatomic ,assign)IBInspectable CGFloat  lineWidth;
@property (nonatomic ,strong)IBInspectable UIColor * lineColor;

@property (nonatomic ,assign)IBInspectable CGFloat  paddingLeft;
@property (nonatomic ,assign)IBInspectable CGFloat  paddingRight;
@property (nonatomic ,assign)IBInspectable CGFloat  paddingTop;
@property (nonatomic ,assign)IBInspectable CGFloat  paddingBottom;

@property (nonatomic ,assign)IBInspectable BOOL  isHorizontal;
@property (nonatomic ,assign)IBInspectable BOOL  isDash;
@property (nonatomic ,assign)IBInspectable CGFloat  dashPointWidth;
@property (nonatomic ,assign)IBInspectable CGFloat  dashSpace;

@end

NS_ASSUME_NONNULL_END

然后再.m文件里面,@implementation前面用IB_DESIGNABLE修饰。

#import "ADHouseSourceDottedLine.h"
IB_DESIGNABLE

@implementation ADHouseSourceDottedLine
- (void)drawRect:(CGRect)rect
{
    [super drawRect:rect];
    CGContextRef currentContext = UIGraphicsGetCurrentContext();
    CGFloat bx = 0,by = 0,ex = 0,ey = 0;
    if (_isHorizontal) {
        bx = _paddingLeft;
        by = rect.size.height * 0.5;
        ex = rect.size.width - _paddingRight;
        ey = by;
    }else
    {
        bx = rect.size.width * 0.5;
        by = _paddingTop;
        ex = bx;
        ey = rect.size.height - _paddingBottom;
    }
    // 画中间虚线
//    CGMutablePathRef path = CGPathCreateMutable();
//    CGPathMoveToPoint(path, NULL, bx, by);
//    CGPathAddLineToPoint(path, NULL, ex, ey);
    
    CGContextMoveToPoint(currentContext, bx, by);
    CGContextAddLineToPoint(currentContext, ex, ey);
    // 2、 添加路径到图形上下文
//    CGContextAddPath(currentContext, path);
    CGContextSetLineWidth(currentContext, _lineWidth / [UIScreen mainScreen].scale);
    CGContextSetStrokeColorWithColor(currentContext, _lineColor.CGColor);
    if (_isDash) {
        CGFloat lengths[] = {_dashPointWidth , _dashSpace};
        CGContextSetLineDash(currentContext, 0, lengths, 2);
    }
    // 4、 绘制图像到指定图形上下文
//    CGContextDrawPath(currentContext, kCGPathFill);
    CGContextStrokePath(currentContext);
}

@end

参考:# 在OC和Swift中使用IBDesignable/IBInspectable
Swift 画线控件(支持xib和storyboard)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,646评论 4 366
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,979评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,391评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,356评论 0 215
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,740评论 3 293
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,836评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,022评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,764评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,487评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,728评论 2 252
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,200评论 1 263
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,548评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,217评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,134评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,921评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,919评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,766评论 2 274

推荐阅读更多精彩内容