圖片水印
圖片水印就是在圖片的基礎(chǔ)上繪制一些文字或logo,最終生成一張新的圖片
-
在viewDidLoad方法中生成圖片水印的步驟:
-
加載圖片
UIImage *image = [UIImage imageNamed:@"小黃人"];
手動(dòng)獲取上下文咪奖,之前的上下文都是在view的drawRect方法中獲取的(跟View相關(guān)聯(lián)的layer上下文)成箫,目前我們需要繪制圖片到新的圖片上,所以需要用到位圖上下文
-
怎樣獲取位圖上下文呢腹殿?注意独悴,位圖上下文的獲取方式跟layer上下文不一樣,位圖上下文需要我們手動(dòng)創(chuàng)建
// 開啟一個(gè)位圖上下文锣尉,注意位圖上下文跟view無(wú)關(guān)聯(lián)刻炒,所以不需要在drawRect. // size:位圖上下文的尺寸(新圖片的尺寸) // opaque: 不透明度 YES:不透明 NO:透明,通常我們一般都弄透明的上下文 // scale:通常不需要縮放上下文自沧,取值為0坟奥,表示不縮放 UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
-
繪制圖片水印
// 1.繪制原生的圖片 [image drawAtPoint:CGPointZero]; // 2.給原生的圖片添加文字 NSString *str = @"水印文字"; // 創(chuàng)建字典屬性 NSMutableDictionary *dict = [NSMutableDictionary dictionary]; dict[NSForegroundColorAttributeName] = [UIColor redColor]; dict[NSFontAttributeName] = [UIFont systemFontOfSize:20]; [str drawAtPoint:CGPointMake(200, 528) withAttributes:dict];
-
-
繪制貝瑟爾路徑
UIBezierPath *path =[UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 300, 300)]; [[UIColor redColor] set]; [path stroke];
-
底層實(shí)現(xiàn)
// 1.獲取上下文(位圖上下文) CGContextRef ctx = UIGraphicsGetCurrentContext(); // 2.描述路徑 CGContextMoveToPoint(ctx, 50, 50); CGContextAddLineToPoint(ctx, 200, 200); [[UIColor redColor] set]; // 3.渲染上下文 CGContextStrokePath(ctx);
-
生成一張圖片給我們,從上下文中獲取圖片拇厢,并將其顯示到UIImageView上
UIImage *imageWater = UIGraphicsGetImageFromCurrentImageContext(); self.imageView.image = imageWater;
-
關(guān)閉上下文
UIGraphicsEndImageContext();
圖片裁剪
- 圖片裁剪是把正方形圖片進(jìn)行裁剪爱谁,并生成一張圓形的圖片
- 裁剪無(wú)邊框的圖片
- (void)clipImage
{
// 0.加載圖片
UIImage *image = [UIImage imageNamed:@"阿貍頭像"];
// 1.開啟位圖上下文,跟圖片尺寸一樣大
UIGraphicsBeginImageContextWithOptions(image.size, NO, 0);
// 2.設(shè)置圓形裁剪區(qū)域孝偎,正切與圖片
// 2.1創(chuàng)建圓形的路徑
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, image.size.width, image.size.height)];
// 2.2把路徑設(shè)置為裁剪區(qū)域
[path addClip];
// 3.繪制圖片
[image drawAtPoint:CGPointZero];
// 4.從上下文中獲取圖片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 5.關(guān)閉上下文
UIGraphicsEndImageContext();
_imageView.image = clipImage;
}
- 裁剪有邊框的圖片访敌,并將裁剪方法放到分類中去,以便他人的使用
+ (UIImage *)imageWithClipImage:(UIImage *)image borderWidth:(CGFloat)borderWidth borderColor:(UIColor *)color
{
// 圖片的寬度和高度
CGFloat imageWH = image.size.width;
// 設(shè)置圓環(huán)的寬度
CGFloat border = borderWidth;
// 圓形的寬度和高度
CGFloat ovalWH = imageWH + 2 * border;
// 1.開啟上下文
UIGraphicsBeginImageContextWithOptions(CGSizeMake(ovalWH, ovalWH), NO, 0);
// 2.畫大圓
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, ovalWH, ovalWH)];
[color set];
[path fill];
// 3.設(shè)置裁剪區(qū)域
UIBezierPath *clipPath = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(border, border, imageWH, imageWH)];
[clipPath addClip];
// 4.繪制圖片
[image drawAtPoint:CGPointMake(border, border)];
// 5.獲取圖片
UIImage *clipImage = UIGraphicsGetImageFromCurrentImageContext();
// 6.關(guān)閉上下文
UIGraphicsEndImageContext();
return clipImage;
}
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 圖片裁剪:把正方形圖片重新生產(chǎn)一張圓形的圖片
// 圖片裁剪
UIImage *image = [UIImage imageWithClipImage:[UIImage imageNamed:@"阿貍頭像"] borderWidth:1 borderColor:[UIColor redColor]];
_imageView.image = image;
}
屏幕截屏
- 屏幕截屏的代碼可以封裝到分類當(dāng)中
+ (UIImage *)imageWithCaputureView:(UIView *)view
{
// 開啟位圖上下文
UIGraphicsBeginImageContextWithOptions(view.bounds.size, NO, 0);
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的圖層渲染到上下文衣盾,layer只能渲染寺旺,不能繪制
[view.layer renderInContext:ctx];
// 生成一張圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();
return image;
}
- 在viewDidLoad方法中調(diào)用分類,返回一個(gè)UIImage對(duì)象
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 生成一張新的圖片
UIImage *image = [UIImage imageWithCaputureView:self.view];
// image轉(zhuǎn)data
// compressionQuality: 圖片質(zhì)量 1:最高質(zhì)量
NSData *data = UIImageJPEGRepresentation(image,1);
[data writeToFile:@"/Users/xiaomage/Desktop/view.png" atomically:YES];
}
圖片截取
- 在viewDidLoad方法中給控制器View添加一個(gè)pan手勢(shì)
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
// 給控制器的view添加一個(gè)pan手勢(shì)
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
}
- 在pan:方法中對(duì)手勢(shì)進(jìn)行處理
- (void)pan:(UIPanGestureRecognizer *)pan
{
CGPoint endA = CGPointZero;
if (pan.state == UIGestureRecognizerStateBegan) { // 一開始拖動(dòng)的時(shí)候
// 獲取一開始觸摸點(diǎn)
_startP = [pan locationInView:self.view];
}else if(pan.state == UIGestureRecognizerStateChanged){ // 一直拖動(dòng)
// 獲取結(jié)束點(diǎn)
endA = [pan locationInView:self.view];
CGFloat w = endA.x - _startP.x;
CGFloat h = endA.y - _startP.y;
// 獲取截取范圍
CGRect clipRect = CGRectMake(_startP.x, _startP.y, w, h);
// 生成截屏的view
self.clipView.frame = clipRect;
}else if (pan.state == UIGestureRecognizerStateEnded){
// 圖片裁剪势决,生成一張新的圖片
// 開啟上下文
// 如果不透明阻塑,默認(rèn)超出裁剪區(qū)域會(huì)變成黑色,通常都是透明
UIGraphicsBeginImageContextWithOptions(_imageV.bounds.size, NO, 0);
// 使用貝瑟爾路徑徽龟,設(shè)置裁剪區(qū)域
UIBezierPath *path = [UIBezierPath bezierPathWithRect:_clipView.frame];
[path addClip];
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 把控件上的內(nèi)容渲染到上下文
[_imageV.layer renderInContext:ctx];
// 生成一張新的圖片
_imageV.image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();
// 先移除
[_clipView removeFromSuperview];
// 截取的view設(shè)置為nil
_clipView = nil;
}
// 獲取手指的偏移量
// pan translationInView:(UIView *)
}
- clipView的懶加載叮姑,僅當(dāng)clipView為空的時(shí)候才去創(chuàng)建,否則直接返回
- (UIView *)clipView{
if (_clipView == nil) {
UIView *view = [[UIView alloc] init];
_clipView = view;
view.backgroundColor = [UIColor blackColor];
view.alpha = 0.5;
[self.view addSubview:view];
}
return _clipView;
}
圖片擦除
- 需要用到手勢(shì)操作
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
[self.view addGestureRecognizer:pan];
}
- pan方法根據(jù)用戶手指的位置進(jìn)行圖片擦除
- (void)pan:(UIPanGestureRecognizer *)pan
{
// 獲取當(dāng)前點(diǎn)
CGPoint curP = [pan locationInView:self.view];
// 獲取擦除的矩形范圍
CGFloat wh = 100;
CGFloat x = curP.x - wh * 0.5;
CGFloat y = curP.y - wh * 0.5;
CGRect rect = CGRectMake(x, y, wh, wh);
// 開啟上下文
UIGraphicsBeginImageContextWithOptions(self.view.bounds.size, NO, 0);
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 控件的layer渲染上去
[_imageView.layer renderInContext:ctx];
// 擦除圖片
CGContextClearRect(ctx, rect);
// 生成一張圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
_imageView.image = image;
// 關(guān)閉上下文
UIGraphicsEndImageContext();
}
手勢(shì)解鎖的界面布局
- 首先,在awakeFromNib中創(chuàng)建9個(gè)按鈕對(duì)象
// 加載完xib的時(shí)候調(diào)用
- (void)awakeFromNib
{
// 創(chuàng)建9個(gè)按鈕
for ( int i = 0; i < 9; i++) {
UIButton *btn = [UIButton buttonWithType:UIButtonTypeCustom];
[btn setImage:[UIImage imageNamed:@"gesture_node_normal"] forState:UIControlStateNormal];
[btn setImage:[UIImage imageNamed:@"gesture_node_highlighted"] forState:UIControlStateSelected];
[self addSubview:btn];
}
}
- 然后传透,在layoutSubviews方法中對(duì)創(chuàng)建的按鈕對(duì)象進(jìn)行布局操作
// 為什么要在這個(gè)方法布局子控件耘沼,因?yàn)橹灰徽{(diào)用這個(gè)方法,就表示父控件的尺寸確定
- (void)layoutSubviews
{
[super layoutSubviews];
NSUInteger count = self.subviews.count;
int cols = 3;
CGFloat x = 0;
CGFloat y = 0;
CGFloat w = 74;
CGFloat h = 74;
CGFloat margin = (self.bounds.size.width - cols * w) / (cols + 1);
CGFloat col = 0;
CGFloat row = 0;
for (NSUInteger i = 0; i < count; i++) {
UIButton *btn = self.subviews[i];
// 獲取當(dāng)前按鈕的列數(shù)
col = i % cols;
row = i / cols;
x = margin + col * (margin + w);
y = row * (margin + w);
btn.frame = CGRectMake(x, y, w, h);
}
}
手勢(shì)解鎖的業(yè)務(wù)邏輯實(shí)現(xiàn)
- 使用可變數(shù)組來(lái)存儲(chǔ)已經(jīng)選中的按鈕
@property (nonatomic, strong) NSMutableArray *selectedsBtn;
// 重寫get方法朱盐,對(duì)數(shù)組進(jìn)行懶加載
- (NSMutableArray *)selectedsBtn
{
if (_selectedsBtn == nil) {
_selectedsBtn = [NSMutableArray array];
}
return _selectedsBtn;
}
- 重寫pan方法群嗤,對(duì)用戶的手勢(shì)進(jìn)行判斷,首先判斷觸摸點(diǎn)是否在按鈕上兵琳,然后當(dāng)用戶手指離開屏幕時(shí)還原界面
- (IBAction)pan:(UIPanGestureRecognizer *)pan
{
// 獲取觸摸點(diǎn)
_curP = [pan locationInView:self];
// 判斷觸摸點(diǎn)在不在按鈕上
for (UIButton *btn in self.subviews) {
// 點(diǎn)在不在某個(gè)范圍內(nèi),并且按鈕沒(méi)有被選中
if (CGRectContainsPoint(btn.frame, _curP) && btn.selected == NO) {
// 點(diǎn)在按鈕上
btn.selected = YES;
// 保存到數(shù)組中
[self.selectedsBtn addObject:btn];
}
}
// 重繪
[self setNeedsDisplay];
if (pan.state == UIGestureRecognizerStateEnded) {
// 創(chuàng)建可變字符串
NSMutableString *strM = [NSMutableString string];
// 保存輸入密碼
for (UIButton *btn in self.selectedsBtn) {
[strM appendFormat:@"%ld",btn.tag];
}
NSLog(@"%@",strM);
// 還原界面
// 取消所有按鈕的選中
[self.selectedsBtn makeObjectsPerformSelector:@selector(setSelected:) withObject:@(NO)];
// 清除畫線,把選中按鈕清空
[self.selectedsBtn removeAllObjects];
}
}
- 根據(jù)被選中按鈕的數(shù)組狂秘,對(duì)界面進(jìn)行重繪操作
// 只要調(diào)用這個(gè)方法,就會(huì)把之前繪制的東西全部清掉躯肌,重新繪制
- (void)drawRect:(CGRect)rect
{
// 沒(méi)有選中按鈕者春,不需要連線
if (self.selectedsBtn.count == 0) return;
// 把所有選中按鈕中心點(diǎn)連線
UIBezierPath *path = [UIBezierPath bezierPath];
NSUInteger count = self.selectedsBtn.count;
// 把所有選中按鈕之間都連好線
for (int i = 0; i < count; i++) {
UIButton *btn = self.selectedsBtn[i];
if (i == 0) {
// 設(shè)置起點(diǎn)
[path moveToPoint:btn.center];
}else{
[path addLineToPoint:btn.center];
}
}
// 連線到手指的觸摸點(diǎn)
[path addLineToPoint:_curP];
[[UIColor greenColor] set];
path.lineWidth = 10;
path.lineJoinStyle = kCGLineJoinRound;
[path stroke];
}
畫板
- 根據(jù)數(shù)組中數(shù)據(jù)的類型,繪制圖片和線條
// 繪制圖形
// 只要調(diào)用drawRect方法就會(huì)把之前的內(nèi)容全部清空
- (void)drawRect:(CGRect)rect
{
for (DrawPath *path in self.paths) {
if ([path isKindOfClass:[UIImage class]]) {
// 繪制圖片
UIImage *image = (UIImage *)path;
[image drawInRect:rect];
}else{
// 畫線
[path.pathColor set];
[path stroke];
}
}
}
- 選擇照片清女,并實(shí)現(xiàn)代理方法钱烟,將選中的照片繪制到畫板上
#pragma mark - 選擇照片
- (IBAction)pickerPhoto:(id)sender {
// 彈出系統(tǒng)的相冊(cè)
// 選擇控制器(系統(tǒng)相冊(cè))
UIImagePickerController *picekerVc = [[UIImagePickerController alloc] init];
// 設(shè)置選擇控制器的來(lái)源
// UIImagePickerControllerSourceTypePhotoLibrary 相冊(cè)集
// UIImagePickerControllerSourceTypeSavedPhotosAlbum:照片庫(kù)
picekerVc.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
// 設(shè)置代理
picekerVc.delegate = self;
// modal
[self presentViewController:picekerVc animated:YES completion:nil];
}
#pragma mark - UIImagePickerControllerDelegate
// 當(dāng)用戶選擇一張圖片的時(shí)候調(diào)用
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
// 獲取選中的照片
UIImage *image = info[UIImagePickerControllerOriginalImage];
// 把選中的照片畫到畫板上
_drawView.image = image;
// dismiss
[self dismissViewControllerAnimated:YES completion:nil];
}
- 保存修改后的照片,相當(dāng)于截屏操作
#pragma mark - 保存
- (IBAction)save:(id)sender {
// 截屏
// 開啟上下文
UIGraphicsBeginImageContextWithOptions(_drawView.bounds.size, NO, 0);
// 獲取上下文
CGContextRef ctx = UIGraphicsGetCurrentContext();
// 渲染圖層
[_drawView.layer renderInContext:ctx];
// 獲取上下文中的圖片
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
// 關(guān)閉上下文
UIGraphicsEndImageContext();
// 保存畫板的內(nèi)容放入相冊(cè)
// image:寫入的圖片
// completionTarget圖片保存監(jiān)聽(tīng)者
// 注意:以后寫入相冊(cè)方法中嫡丙,想要監(jiān)聽(tīng)圖片有沒(méi)有保存完成拴袭,保存完成的方法不能隨意亂寫
UIImageWriteToSavedPhotosAlbum(image, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
}
// 監(jiān)聽(tīng)保存完成,必須實(shí)現(xiàn)這個(gè)方法
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo
{
NSLog(@"保存圖片成功");
}