前几天遇到一个问题,将uint32_t类型的值放到字典里面传递到下一个vc中。就这么个简单的问题,却踩进了坑。

由于之前主攻java,想当然的就将uint32_t转换成NSString然后丢到下一个vc中,然后要使用的时候再转回uint32_t,然后问题就出现了:转回uint32_t后发现数值不正确。

下面是之前的转换方式:

//uint32_t --> NSString
uint32_t a=2373916479;
NSString * aStr=[NSString stringWithFormat:@"%u",a];//这个没毛病,确实变成了字符串
//NSString --> uint32_t
uint32_t b=[aStr intValue];//这句话有问题

通过上面的代码执行后发现a和b的值不一样,惊到我了!

通过查询uint32_t的定义,发现这货就是unsigned int。然后再回来看代码,觉得没问题哇。。。

然后再转念一想,它是无符号的int,直接转数值会有损。于是将最后一句改成:

uint32_t b=[aStr longLongValue];

虽然执行结果正确了,但是xcode提示了一个警告:

Implicit conversion loses integer precision:'long long' to 'uint32_t' (aka 'unsigned int')

虽然能解决问题,但是这个警告实在是看的很不爽,而且实现起来比较暴力。怎样才能更加优雅的解决目前的困扰呢?

搜索了StackOverflow,突然发现可以把uint32_t放到NSNumber中,再放到字典里面,于是:

//uint32_t --> NSNumber
uint32_t a=2373916479;
NSNumber * aNumber=[NSNumber numberWithUnsignedInt:a];//用NSNumber包裹uint32_t,这样就能放到字典中了
//NSNumber --> uint32_t
uint32_t b=[aNumber unsignedIntValue];//结果正确

经过上面的实践之后,总结一下:

  1. 一切都是基础不扎实导致的
  2. uint32_t本质是unsigned int,如果先转化为字符串再用intValue转回来就会有可能出现数据不正确的情况,而且NSString也没有提供unsingedIntValue,所以使用NSString不明智;
  3. NSNumber是一个可以放到字典和数组中的类型。如果需要再字字典或者数组中存放基本数据类型,可以把所需要的基本数据类型转化为NSNumber后再存入

最近有这么个给图片加水印的需求,把网上的方法基本都试了个遍,最后也就发现一个简单的方法比较可靠。改造了一下,变成了下面这样的:

#define UIColorFromHex(s)  [UIColor colorWithRed:(((s & 0xFF0000) >> 16))/255.0 green:(((s &0xFF00) >>8))/255.0 blue:((s &0xFF))/255.0 alpha:1.0]
-(UIImage *)watermarkImage:(UIImage *)img withName:(NSString *)name
{
    NSString* mark = name;
    const NSString * copyRight=@"©2017 测试公司.All rights reserved.";
    int w = img.size.width;
    int h = img.size.height;
    UIGraphicsBeginImageContext(img.size);
    [img drawInRect:CGRectMake(0, 0, w, h)];
    UIFont * font=[UIFont systemFontOfSize:64];
    NSShadow *shadow = [[NSShadow alloc] init];
    shadow.shadowColor = UIColorFromHex(0x666666);
    shadow.shadowBlurRadius = 5;
    shadow.shadowOffset = CGSizeMake(1, 3);
    NSDictionary *attr_mainContent = @{
                           NSFontAttributeName: font,  //设置字体
                           NSForegroundColorAttributeName: [UIColor cyanColor] ,  //设置字体颜色
                           NSShadowAttributeName:shadow,
                           NSVerticalGlyphFormAttributeName:@0
                           };
    NSDictionary *attr_copyRight = @{
                                       NSFontAttributeName: font,  //设置字体
                                       NSForegroundColorAttributeName: [UIColor whiteColor] ,  //设置字体颜色
                                       NSBackgroundColorAttributeName:[UIColor grayColor]
                                       };
    NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
    CGSize main_Size1 = [name boundingRectWithSize:CGSizeMake(MAXFLOAT, 0.0) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;
    CGSize main_Size2 = [name boundingRectWithSize:CGSizeMake(w, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;

    CGSize copyRight_Size1 = [copyRight boundingRectWithSize:CGSizeMake(MAXFLOAT, 0.0) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;
    CGSize copyRight_Size2 = [copyRight boundingRectWithSize:CGSizeMake(w, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;

    //=======
    //draw main text
    [mark drawInRect:CGRectMake(20, h - 20 - main_Size2.height - copyRight_Size2.height, main_Size1.width, main_Size2.height) withAttributes:attr_mainContent];//左下角
    //=======
    //draw copy right text
    //copyRight
    [copyRight drawInRect:CGRectMake(0, h - copyRight_Size2.height , copyRight_Size1.width, copyRight_Size2.height) withAttributes:attr_copyRight];//左下角

    UIImage *aimg = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    return aimg;
}

效果如下:(iPhone 7Plus的大小)

我在这里面加入了两段水印,上面的是要加入的信息,下面是版权信息。

NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithObject:font forKey:NSFontAttributeName];
CGSize main_Size1 = [name boundingRectWithSize:CGSizeMake(MAXFLOAT, 0.0) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;
CGSize main_Size2 = [name boundingRectWithSize:CGSizeMake(w, MAXFLOAT) options:NSStringDrawingUsesLineFragmentOrigin attributes:dic context:nil].size;

这一段代码用来获取要添加的水印字符的宽度和高度。main_Size1的width就是文本的宽度,main_Size2的height就是文本的高度。这里踩过一个坑,之前照抄网上的获取宽度和高度的代码,发现添加上去的水印文字总是显示不全,后来测试了才发现,需要指定一个宽度,假设为x,这样获取到的高度是文本最大宽度为x的情况下对应的高度(汗颜,仔细想想确实应该是这样)。如果将文本的宽度指定为图片的宽度的话,那么基本可以保证文字的正常显示。

[mark drawInRect:CGRectMake(20, h - <height>, <width>, <height>) withAttributes:<Your Attr>];

这一段是最核心的部分,用于将文本绘制到图片上。可以绘制到由UIGraphicsBeginImageContext指定的区域中的任意位置。代码中我设置绘制区域为整个图片,水印设置在左下角。

这里有一个Attributes参数,是一个属性数组。其中可用的属性如下:

  • NSKernAttributeName:设置字符间距,取值为 NSNumber 对象(整数),正值间距加宽,负值间距变窄
  • NSFontAttributeName:设置文本的字体,参数为UIFont对象
  • NSForegroundColorAttributeName:设置文本的颜色,参数为UIColor
  • NSParagraphStyleAttributeName:设置段落格式。参数为NSMutableParagraphStyle或者NSParagraphStyle对象。例如:
    NSMutableParagraphStyle *paragraph = [[NSMutableParagraphStyle alloc] init];
    paragraph.alignment = NSTextAlignmentCenter;//居中
  • NSBackgroundColorAttributeName:设置文本的背景颜色,参数为UIColor
  • NSStrokeWidthAttributeName:设置文本的描边宽度。

    这个属性所对应的值是一个 NSNumber 对象(小数)。该值改变描边宽度(相对于字体size 的百分比)。默认为 0,即不改变。正数只改变描边宽度。负数同时改变文字的描边和填充宽度。例如,对于常见的空心字,这个值通常为3.0。

    如果同时设置了空心的两个属性,并且NSStrokeWidthAttributeName属性设置为整数,那么文字前景色就无效果了

  • NSStrokeColorAttributeName:设置文本的描边颜色
  • NSStrikethroughStyleAttributeName:设置删除线。参数为数字。例如:NSStrikethroughStyleAttributeName:@3 表示删除线的宽度为3
  • NSUnderlineStyleAttributeName:添加下划线。参数为枚举类型。可用的类型如下:

    NSUnderlineStyleNone

    NSUnderlineStyleSingle   

    NSUnderlineStyleThick

    NSUnderlineStyleDouble

    设置单下划线:NSUnderlineStyleAttributeName:@(NSUnderlineStyleSingle)

  • NSUnderlineColorAttributeName:设置下划线颜色,参数为UIColor
  • NSShadowAttributeName:设置阴影效果,参数为NSShadow对象。注意这个属性必需和这三个属性NSVerticalGlyphFormAttributeName,NSObliquenessAttributeName和NSExpansionAttributeName之一同时使用才能生效,否则将无效。

    关于NSShadow的用法,如下:

    NSShadow *shadow = [[NSShadow alloc] init];//初始化
    shadow.shadowColor = [UIColor grayColor];//设置阴影颜色
    shadow.shadowBlurRadius = 2;//模糊程度,值越大越模糊
    shadow.shadowOffset = CGSizeMake(2, 4);//阴影的位置。如:设置为CGSizeMake(0, 0)则会直接叠加到文本后方;CGSizeMake(0, 4)则是出现在文本的下方
  • NSVerticalGlyphFormAttributeName:该属性所对应的值是一个 NSNumber 对象(整数)。0 表示横排文本。1 表示竖排文本。在 iOS 中,总是使用横排文本,0 以外的值都未定义。
  • NSObliquenessAttributeName:设置字体倾斜,参数为NSNumber 0或1
  • NSExpansionAttributeName:设置文本扁平化(个人感觉很丑,而且还会影响文本的宽度计算),参数为NSNumber 0或1
  • NSTextEffectAttributeName:设置文本特殊效果,取值为 NSString 对象,目前只有图版印刷效果可用
  • NSBaselineOffsetAttributeName:设置基线偏移值,取值为 NSNumber (float),正值上偏,负值下偏
  • NSWritingDirectionAttributeName:设置文字书写方向,从左向右书写或者从右向左书写。以下是API中的说明:

    NSNumbers representing the nested levels of writing direction overrides as defined by Unicode LRE, RLE, LRO, and RLO characters. The control characters can be obtained by masking NSWritingDirection and NSWritingDirectionFormatType values.

    LRE: NSWritingDirectionLeftToRight|NSWritingDirectionEmbedding
    RLE: NSWritingDirectionRightToLeft|NSWritingDirectionEmbedding
    LRO: NSWritingDirectionLeftToRight|NSWritingDirectionOverride
    RLO: NSWritingDirectionRightToLeft|NSWritingDirectionOverride

  • NSLinkAttributeName:设置链接属性,取值为NSURL对象。点击后调用浏览器打开指定URL地址
  • NSAttachmentAttributeName:设置文本附件,取值为NSTextAttachment对象,常用于文字图片混排

基本就以上这些属性

PS:UILabel也是可以设置以上属性,只是代码不一样而已。关于这个可以参考:http://www.jianshu.com/p/6665c088bd01

之前考虑用MMPopupView来实现弹出式对话框,不得不说这个弹出的效果还是很不错的,可惜我发现使用xib来绘图还是有点问题的,所以想了个另类的办法:弹出一个模态的view controller来充当对话框。

成功实现之后,发现无法改变背景透明度,即弹出动画显示的过程中背景是有透明色的,动画过后就变成不透明了。于是在网上搜索到了这样的解决办法:

参考:http://www.jianshu.com/p/4f7a29bb333f

AppDelegate *appdelegate=(AppDelegate*)[[UIApplication sharedApplication] delegate];

UIViewController *vc = [[UIViewController alloc]init];//这里写你要弹出的vc

appdelegate.window.rootViewController.definesPresentationContext = YES;

view.modalPresentationStyle = UIModalPresentationOverCurrentContext;

[appdelegate.window.rootViewController presentViewController:vc animated:YES completion:^{

vc.view.backgroundColor=[UIColor colorWithRed:237/255.0 green:236/255.0 blue:244/255.0 alpha:0.5];//这里写你需要的颜色,或者透明度

}];

这样基本上就可以了。如果要更加完美的话,可以修改弹出动画的背景透明度,和上述的颜色一致,这样就可以让弹出效果更自然

在Android中,Activity指代一个界面,要跳转到某个指定的界面,方法就这么俩个:startActivity和startActivityForResult,参数可以通过Intent来传送。

但是,在iOS中!这个界面跳转方法真的是非常多样化,传参的方式也是五花八门。如果各种方法混用的话,简直要让人抓狂。故在此进行整理并mark一下

①:两个界面都在一个storyboard上,且已经进行连线

举个例子,如图:

我们在storyboard上面,对着prototype cell连线到另外一个view controller,表示这个tableview的cell点击事件是跳转到另外一个view controller,代码中就不需要写tableview的点击事件了。

如果需要传参数的话,需要给这根线设置identifier:

然后重写一个prepareForSegue方法对目标view controller传参(此处采用属性的方式来传参,省时省力,当然也可以换成其他传参方法,但一定要在这个方法里面处理):

这里只要处理传参,不要关心跳转的问题,因为跳转的部分storyboard已经帮你处理了。

②:同样两个界面都在storyboard上面,但是没有进行连线

通常出现这种情况是因为两个界面在不同的storyboard上,或者是因为某些原因,虽然在同一个storyboard上但是没有连线。此时,我们的处理方式要作出一些变化了。

以①中的情况举例,需要重写一个tableview cell的点击事件:

然后需要在storyboard上对目标的view controller设置一个 storyboard ID。这个id要和上面代码中设置的一致,不然就找不到这个view controller

这样就完成了跳转+传参。

注意,按照我上面方式来写的话,传参和跳转的逻辑都已经包含在了点击事件中,所以就没有必要去重写prepareForSegue了,因为prepareForSegue根本就不会被调用。

③:一个界面在storyboard中,另一个在xib上

这种情况处理起来最简单,直接在点击事件中,把目标view controller new出来,设置属性(传参)然后调用

[self.navigationController pushViewController:XXX animated:YES];

就能进行跳转

④:两个都是xib

同③的处理方法

⑤:手动跳转

这种情况下是将两个view controller连线并设置id,在代码中手动控制跳转。

跳转代码:

[self performSegueWithIdentifier:@"XXXXXX" sender:nil];

传参的话,也要通过prepareForSegue进行。

⑥:纯代码方式,没有xib,没有storyboard:【待补充】

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

总结一下,跳转的代码

在storyboard上寻找指定的view controller:

UIStoryboard *story = [UIStoryboard storyboardWithName:@”MyStoryboardName“bundle:[NSBundle mainBundle]];
MyViewController *storVC = [story instantiateViewControllerWithIdentifier:@”MyViewController “];

通过storyboard进行跳转界面的传参,需要在prepareForSegue中进行参数设置

手动跳转:performSegueWithIdentifier

在上篇文章中,讲了如何用代码来加载xib到app中,这篇讲一下如何在Storyboard上进行调用

首先在Storyboard上建立一个view controller,然后从右侧拖一个view到界面上来

为了偷个懒,就沿用上篇文章中的Test4.xib和Test4ViewController

一、当Test4.xib的file’s owner设置为了Test4

这里需要对Test4进行一些改造,如图:

添加了两个方法,initWithCoder是用于storyboard加载view时候会调用的,这个是目前的关键(注意那个owner是self),上面一个initWithFrame是代码加载view时候调用的,storyboard会视而不见;

再移除Test4ViewController中viewDidLoad中的自定义代码后,运行:

二、当对Test4设置view的custom class时:

【我还没见到这种做法的,暂时就不考虑了】

 

先介绍一种最简单的调用方式

方法一:直接引用

假设我制作了一个名为Test3.xib的xib,然后随便拖了几个控件,如图

创建一个view controller,假设就叫Test3ViewController,在storyboard上拖一个view controller出来,设置Custom class为Test3ViewController

然后在Test3ViewController里面调用它,只是让它显示出来,不改变任何属性:(注意高亮的地方,后面会讲原理)

运行结果:

look,加载上去了。这种加载方式的优点是。。超级简单。缺点是由于布局被写死,开发中基本上不会这么去用

接下来讲一个稍微高级一点的

方法二:属性可变

同样拿上面的Test3.xib来实验,现在对这个xib设置一个files’s owner为Test3ViewController,如图

为了方便看效果,在storyboard上面添加一个按钮,连接action

会自动在m文件里面生成这么个方法,先放着不管

打开Test3.xib,切换到分屏,把两个label连线到Test3ViewController.h上面,命名为xibLabel1和xibLabel2,如图

现在在Test3ViewController.m里面的changeLabel这个按钮事件里添加两行代码(改变xib里面的两个label文字):

运行~咦报错了

这里要注意,因为添加了IBOutlet,所以需要将之前加载xib方法改一参数,看圈里面的

把之前的nil改成self就可以了。原因在于,xib设置了file’s owner,而同时有IBOutlet,所以,这里也要改成和xib设置的相同,此处是self,不然就报错

修改之后再次运行:

点击前的效果👇

点击之后的效果👇

现在来更加炫酷一点的

方法三:自定义class(姑且这么称呼)

这里还有三种办法来达到效果,但是主要是前面两种方案

① XIB 中的 UIView 控件与类关联

为了和上面的作区分,我新建一个xib叫Test4,同时新建Test4.h和Test4.m以及新的view controller

接下来是关键一步。这里不是设置file’s owner,而是设置根view的custom class

添加一个label,连接IBOutlet到Test4上

然后view controller中这么调用

我们来运行一下:

OK,目标达成!

日常使用可以对Test4进行相应的封装,举个例子,我在Test4.h中定义一个初始化方法:

那么,view controller中就调用自己定义的方法:

解释:

这种方式会通过 initWithCoder: 方法来从 XIB 文件初始化该 view。使用的时候我们可以通过 awakeFromNib 方法来做进一步处理。

通过这种方式需要注意的是在initWithCoder:方法中,并没有建立 XIB 和 TestView 的 IBOutlet 连接,所以在这里通过代码引用 XIB 中的控件是引用不到的,而当awakeFromNib执行的时候,各种 IBOutlet 都连接好了。所以如果有子控件的初始化工作,最好放在awakeFromNib里面。

② 通过 File’s owner 来关联

如图:

拖一个IBOutlet到Test4上面。

注意,这种方式创建 view 需要手动来初始化,所以Test4.m中的代码要这么写:

自己定义了一个加载方法为

- (instancetype)initFromNibWithFrame:(CGRect)frame

还添加了一个加载其他设置的方法,初始化可以在这里面进行,这个时候 XIB 和 Test4 的 IBOutlet 连接也已经建立好了.👇

-(void) nib_viewDidLoad

Test4ViewController中的调用是这样的:

CGRectMake中我随便写了个大小,运行:

补充:也可以直接在initWithFrame中进行加载,这样可以省去自定义方法,直接使用 alloc init即可。

(以上这两种方法参考自 独奏 非常感谢这位哥们给我提供了怎么做的思路)

BTW:这位哥们提到了有缺点,我认为如果owner直接写self,即自定义的class的话,②号方法依然是有效的。

③ 邪门歪道法(由于这个是我刚开始的时候自己想出来的,我非常不推荐这么做

在上面的基础上,我们新建一个叫Test3的class

删除之前的IBOutlet,同时把Test3ViewController里面的属性也删除(这里不上图了)

然后把Test3.xib的file’s owner改为Test3,把两个label连接到Test3上

现在,修改Test3ViewController,如下所示:

解释:这里使用了import把Test3引入,创建了Test3的一个对象,并且把这个对象传入到owner这里,同时把xib里面的UILabel的作用域扩展到全局,在按钮点击事件中修改label的文字

运行结果依然是可以达到正确的效果,我就不上图了。

到这里,你已经向自定义控件迈出第一步了 :)


加载xib的核心部分就是这么个方法:

NSArray *views =  [[NSBundle mainBundle] loadNibNamed:@”MyXIB” owner:nil options:nil];

返回值,是xib里面根view的数组。什么叫根view,举个例子,看图

所谓的根view,就是图上箭头所指的。一个xib文件可以包含不止一个根view(我也是后来才发现的)。所以,返回的数组就包含了xib所有的根view。要加载哪个view,取决于你的需求了。

所以,不要迷信网上的写法,比方说这样

AAView *aaView = [[[NSBundle mainBundle] loadNibNamed:@”AAView” owner:self options:nil] lastObject];

比方说,这样

UIView *view = [[[NSBundle mainBundle]loadNibNamed:@”CustomizedView” owner:self options:nil]objectAtIndex:0];

然后来解释一下这个方法的三个参数

第一个参数,name,很简单,就是xib的文件名,不带后缀的

第二个参数,owner,这个是什么呢?看图👇(原文链接:http://blog.csdn.net/xn4545945/article/details/31786391

这里补充一点,如果并没有在xib中建立IBOutlet或者action的话,这个owner就无所谓了,可以填写nil

第三个参数,options,当xib文件开始时,需要的数据(咱也不懂)


关于如何获取根视图里面的子视图,如果file’s owner是别的class的话,有几种办法:

1. 像我上面讲到的图中的,直接引用 xibClass.xibLabel1

2. 给子视图加上tag,通过tag来找到子视图

代码中这么写:UILabel * targetLabel=[parentView viewWithTag:<你设置的tag>];

如果file’s owner是view controller的话,那就直接用IBOutlet拉属性吧!

1.如何新建xib?

xcode8中新建project默认是建立storyboard来管理界面,这是apple推荐的方式,如果要新建xib的话:

方法一:

选择empty,就可以直接建立一个空的xib

这个xib是空的,可以自己添加根view

方法二:

新建的时候选择这个

这样创建出来的xib默认是带了个根视图view的

以上两个方法是只创建xib,还有一种捆绑式创建的方法

方法三:我称之为捆绑式创建

勾选 Also create XIB file,点击next就好了


创建好xib之后,可以对根view进行一些调整,我喜欢以storyboard为主,辅以xib调用

如何调整,看图

选中了根视图之后,右侧的选项,默认都是Inferred。

第一个选项Size是指大小尺寸,如果设置为Freeform,就可以随意调整大小,默认是屏幕大小

第二个选项是指状态栏,None就是去掉状态栏,否则就是显示状态栏

下面俩,没尝试过

 

  1. 点击图表的柱状图,没有回调代理方法:新版的Charts使用的是下面这两个代理方法,网上流传的有些是旧版的,照抄就掉坑里了

    – (void)chartValueSelected:(ChartViewBase * __nonnull)chartView entry:(ChartDataEntry * __nonnull)entry highlight:(ChartHighlight * __nonnull)highlight

    {

        NSLog(@”chartValueSelected,%f”,[entry y]);

    }

    – (void)chartValueNothingSelected:(ChartViewBase * __nonnull)chartView

    {

        NSLog(@”chartValueNothingSelected”);

    }

    建议看一下ChartViewDelegate的定义

  2. 有些柱状图点击了反而调用了chartValueNothingSelected,没有去调chartValueSelected,这是因为在设置数据源的时候,没有对数据进行x轴方向上的排序。此乃一大坑也,坑了我好久