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

#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];//这里写你需要的颜色,或者透明度

}];

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

本文原引出处:http://wpchina.org/how-to-change-wordpress-domain-prefectly-1528/

UPDATE wp_options SET option_value = replace(option_value, ‘<你的旧域名>’,'<新域名>’) ;
UPDATE wp_posts SET post_content = replace(post_content, ‘<你的旧域名>’,'<新域名>’) ;
UPDATE wp_comments SET comment_content = replace(comment_content, ‘<你的旧域名>’, ‘<新域名>’) ;
UPDATE wp_comments SET comment_author_url = replace(comment_author_url, ‘<你的旧域名>’, ‘<新域名>’) ;

上文中是基于phpMySql的,其中有几个注意要点:

0.写在前面:数据库要备份要备份要备份!万一操作失误,那损失就大了

1.一定要选中数据库之后,再去点击右侧上方的SQL选项卡

2.UPDATE 后面的是表的名称。如果在安装wordpress时自定义过表名称,这里要修改成实际的表名称

3.替换前最好先看看wp_options(也许叫别的名称)中的home字段是什么,然后用这个字段值去替换例子中的www.mydomain.com

4.为了防止出错,修改好sql语句之后,最好先点击右下角的模拟查询按钮。没有这个按钮?请检查sql语句是否正确,否则请升级phpMyAdmin。如果模拟查询执行后的结果有错误,说明sql语句哪里有问题,需要一个一个排查直至问题解决。确认无误后再点击执行按钮。

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

至于文中有人评论说:”二级站点只能访问首页,其他还是转跳到原来的域名地址“,我表示这人maybe是改错了什么地方,允悲

我已经按照这个方法更换过N个域名了,貌似还没出过问题

在做Android开发之后,你会发现随着时间的推移,运行过越来越多的demo,用过越来越多的三方library,c盘空间也越来越少。查看个人用户文件夹,可以发现几个和Android有关的文件夹体积那可是真庞大。下面将会针对各个文件夹给出相应的办法(Windows版)

一、.android文件夹

这个文件夹主要用于保存adb调试用的key和avd模拟器,lldb也要使用这个文件夹

迁移方式1:添加ANDROID_SDK_HOME环境变量,指向你所要保存的地方。

举例:把用户目录下的.android搬到D:\test目录下,ANDROID_SDK_HOME就填写D:\test。最终结果是下次android studio启动后,.android文件夹就会出现在D:\test目录下

缺点:adb调试用的key无法迁移到新目录下,如果用户目录下.android文件夹不存在,每次使用adb命令后会再次生成此文件夹,并重新生成一个adb调试key

迁移方式2:利用符号链接 ,注意这里有两个点符号,一个都不能少,且要链接的地方文件夹必须存在,名称可以随意

mklink /d %USERPROFILE%\.android. <要链接到的地方>

这样就会创建一个符号链接,让android以为这个文件夹还在原来的地方,但其实已经哈哈哈

举例:mklink /d %USERPROFILE%\.android. D:\AndroidFiles

解释:对.android文件夹做一个符号链接到D盘的AndroidFiles文件夹,所有对.android文件夹里面文件的读写其实是在对D盘的AndroidFiles文件夹内容读写

二、.gradle文件夹

这个文件夹很变态,当你使用过某一个版本的gradle后,该版本的gradle就会被下载到这里面。同时使用gradle依赖的三方library也会被下载并保存其中。说穿了它就是一个本地的仓库,这个仓库理论上容量无限大,所以当然要搬走

迁移方式1:新建一个GRADLE_USER_HOME环境变量,指向你想要的位置,然后把.gradle目录剪切过去就ok了

这种迁移方式不会有残留,很干净

迁移方式2:符号链接,在原地留下一个符号链接,不占用c盘空间

三、.AndroidStudioXXXX文件夹

这是android studio的配置文件目录,容量可上G。这个文件夹无法通过改环境变量来达到迁移的目的

迁移方式1:修改android studio的配置,位于安装目录下的bin/idea.properties。感兴趣的可以去改

缺点:不能完全迁移,总有残留,而且每次升级studio后此文件都会被覆盖回去

迁移方式2:还是用符号链接

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

这样看来,符号链接好像是万能的了。。。

但其实不然,符号链接需要被链接的文件或文件夹必须存在,否则将出问题

网上找了一圈没找到特别好的办法。个人比较偏向于插件法,但是插件总有停止维护的一天,不能总是依赖插件

贴一个修改functions.php的办法:

add_filter('upload_mimes''wpdit_filter_mime_types');
functionwpdit_filter_mime_types($mimes)
{
    $mimes['ttf'] = 'font/ttf';
    $mimes['woff'] = 'font/woff';
    $mimes['svg'] = 'font/svg';
    $mimes['eot'] = 'font/eot';
    return$mimes;
}
在wp-include文件夹下面的functions.php文件中添加这个,就能让wordpress支持这些类型的文件上传
注意1:光这样做只是让wordpress认可这些类型,web服务器可能还是不认可,所以需要在ngix或者apache,或者其他的web服务器上添加上对应的mime类型才ok
注意2:每次WordPress升级后,functions.php文件会被覆盖,所以每次升级后都需要手动修改,这是最麻烦的地方

在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拉属性吧!