iOS開發Quartz2D 十三:畫板塗鴉


 

一:效果如圖:

 

二:代碼

#import "ViewController.h"
#import "DrawView.h"
#import "HandleImageView.h"

@interface ViewController ()<UINavigationControllerDelegate,UIImagePickerControllerDelegate,handleImageViewDelegate>
@property (weak, nonatomic) IBOutlet DrawView *drawView;

@end

@implementation ViewController

/**
 *    1:屬於誰的東西,應該在誰里面去寫,盡量減少控制器中的代碼。因為設置的都在self.drawView上進行繪制的
 *
 */

//清屏
- (IBAction)clear:(id)sender {
    [self.drawView clear];
}

//撤銷
- (IBAction)undo:(id)sender {
    [self.drawView undo];
}

//橡皮擦
- (IBAction)erase:(id)sender {
    [self.drawView erase];
}

//設置線的寬度
- (IBAction)setLineWidth:(UISlider *)sender {
    [self.drawView setLineWith:sender.value];
}

//設置線的顏色
/**
 *sender.backgroundColor:調用的btn的get方法獲得是背景色,點擊不同的按鈕,將不同按鈕的背景色傳遞給self.drawView
 */
- (IBAction)setLineColor:(UIButton *)sender {
    [self.drawView setLineColor:sender.backgroundColor];
}

/**
 *    1:UIImagePickerController:能更改中文,但是需要遵守UINavigationControllerDelegate,UIImagePickerControllerDelegate兩個協議,可以指定sourceType,delegate,presentViewController彈出,在代理可以根據sourceType類型做判斷,必須手動去dissmiss
 *
 */
//照片
- (IBAction)photo:(id)sender {
    //從系統相冊當中選擇一張圖片
    //1.彈出系統相冊
    UIImagePickerController *pickerVC = [[UIImagePickerController alloc] init];
    
    //設置照片的來源
    pickerVC.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
    pickerVC.delegate = self;
    //modal出系統相冊
    [self presentViewController:pickerVC animated:YES completion:nil];
}

/**
 * 1: 不用ImageView的原因是,ImageView經過手勢縮放等處理后,不知道了其實際的尺寸,所以用UIView中放UImageView,上下文的大小就為UIView大小
   2:要想保存圖片,先要生成圖片,開啟上下文,將drawView的layer繪制到上下文中(將UIView繪制到drawView的上下文中),得到圖片之后UIImageWriteToSavedPhotosAlbum,寫入系統的相冊,注意:@selector里面的方法不能夠瞎寫,必須得是image:didFinishSavingWithError:contextInfo:
 *
 */
//保存
- (IBAction)save:(id)sender {
    //把繪制的東西保存到系統相冊當中
    
    //1.開啟一個位圖上下文
    UIGraphicsBeginImageContextWithOptions(self.drawView.bounds.size, NO, 0);
    
    
    //2.把畫板上的內容渲染到上下文當中
    CGContextRef ctx =  UIGraphicsGetCurrentContext();
    [self.drawView.layer renderInContext:ctx];
    
    //3.從上下文當中取出一張圖片
    UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
    
    //4.關閉上下文
    UIGraphicsEndImageContext();
    
    //5.把圖片保存到系統相冊當中
    //注意:@selector里面的方法不能夠瞎寫,必須得是image:didFinishSavingWithError:contextInfo:
    UIImageWriteToSavedPhotosAlbum(newImage, self, @selector(image:didFinishSavingWithError:contextInfo:), nil);
    
    
}

//保存完畢時調用
- (void)image:(UIImage *)image didFinishSavingWithError:(NSError *)error contextInfo:(void *)contextInfo {
    NSLog(@"success");
    
}
//- (void)saveSuccess {
//    NSLog(@"success");
//}




- (void)viewDidLoad {
    [super viewDidLoad];
   
}

#pragma mark -- 隱藏狀態欄
- (BOOL)prefersStatusBarHidden {
    return YES;
}

/**
 *1:打印info信息:通過key值獲取image,將image轉化成二進制流的NSData形式,UIImagePNGRepresentation,UIImageJPEGRepresentation,兩種轉化方式,png保持原圖不壓縮,內存大,JPEG可以限制壓縮系數,越大圖片越不清晰,轉化成NSData,writeToFile寫入桌面
  2:先用一個UIView,添加到self.view上,UIView再添加UIImageView,再將整個View的layer繪制到上下文中,UIView設置成透明色,則UIView下面的路徑就會顯示出來了
 *
 */
//當選擇某一個照片時,會調用這個方法
-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {

    NSLog(@"%@",info);
    UIImage *image  = info[UIImagePickerControllerOriginalImage];

    //NSData *data = UIImageJPEGRepresentation(image, 1);
    NSData *data = UIImagePNGRepresentation(image);
    //[data writeToFile:@"/Users/xiaomage/Desktop/photo.jpg" atomically:YES];
    [data writeToFile:@"/Users/xiaomage/Desktop/photo.png" atomically:YES];
    
    HandleImageView *handleV = [[HandleImageView alloc] init];
    handleV.backgroundColor = [UIColor clearColor];
    handleV.frame = self.drawView.frame;
    handleV.image = image;
    handleV.delegate = self;
    [self.view addSubview:handleV];
    
    
    
    
    //self.drawView.image = image;
    //取消彈出的系統相冊 
    [self dismissViewControllerAnimated:YES completion:nil];
    
    
}


-(void)handleImageView:(HandleImageView *)handleImageView newImage:(UIImage *)newImage {
    
    self.drawView.image = newImage;
    
}



- (void)pan:(UIPanGestureRecognizer *)pan {
    
    CGPoint transP = [pan translationInView:pan.view];
    pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y);
    
    //復位
    [pan setTranslation:CGPointZero inView:pan.view];
    
}

/**
 *    初始界面的搭建:
 1:狀態欄的改變:1:全局狀態欄的設定,配合pilist中設置viewcontrollerbase 設置其值為no 2:在控制器中實現方法,prefersStatusBarHidden,和狀態欄樣式的方法
 2:界面的搭建:在搭建控制器或是view的界面的時候:1:先分析界面的UI結構由哪些控件組成,采用分層封裝的思想,將控制器或是view的零散控件封裝在一個整體的模塊中(xib也類似),如何分層?參考新浪微博cell的分層封裝,如何封裝view,參考view的封裝方法.2:若是項目中都會用到這些模塊,就想到抽成父類,讓子類去繼承,不同的部分分別在子類中實現,若是父類想和子類進行關聯,則父類提供方法供子類去重寫,則父類也可以拿到該值。
 
 3:1:對於頂部的UI,可以采用view封裝,但是要考慮橫屏豎屏的適配,很麻煩,所以可以用toolBar控件,里面自動適配toolBar中的按鈕,拖一個toolBar,往里面添加toolBaritem,保存按鈕的添加,就添加一個彈簧,還可以對彈簧進行設置,就會出現上圖的效果。2:底部UI的構建:1:三個按鈕橫屏豎屏的適配,先設置左側按鈕左間距底部間距,右側間距,中間按鈕右側間距底部間距,最右側按鈕右側間距底部間距,三個按鈕等寬,則可以實現三個按鈕平分屏幕的寬度,在設置按鈕的高度,slider設置,左右頂部高度,大view不用設置高度了,其高度會等於所有子控件的高度之和。2:ios9新出來一個控件,StackView,可以解決三個按鈕的適配,直接將按鈕拖進去,設置間距就可以了,自動適配
 
 2:在拖線設置的時候,按住shift鍵,再拖線同時設置多個間距
 
 */
@end
#import <UIKit/UIKit.h>

@interface DrawView : UIView

//清屏
- (void)clear;
//撤銷
- (void)undo;
//橡皮擦
- (void)erase;
//設置線的寬度
- (void)setLineWith:(CGFloat)lineWidth;
//設置線的顏色
- (void)setLineColor:(UIColor *)color;

/** 要繪制的圖片 */
@property (nonatomic, strong) UIImage * image;



@end
#import "DrawView.h"
#import "MyBezierPath.h"

@interface DrawView()

/** 當前繪制的路徑 */
@property (nonatomic, strong) UIBezierPath *path;

//保存當前繪制的所有路徑
@property (nonatomic, strong) NSMutableArray *allPathArray;

//當前路徑的線寬
@property (nonatomic, assign) CGFloat width;

//當前路徑的顏色
@property (nonatomic, strong) UIColor *color;

@end

@implementation DrawView


- (NSMutableArray *)allPathArray {
    
    if (_allPathArray == nil) {
        _allPathArray = [NSMutableArray array];
    }
    return _allPathArray;
}

/**
 *    1:一般一次性設置的內容都放在初始化方法init中或是awakefromNib中
    2:添加拖拽pan手勢用於繪圖
 */
- (void)awakeFromNib {
    //添加手勢
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(pan:)];
    [self addGestureRecognizer:pan];
    
    self.width = 1;
    self.color = [UIColor blackColor];
}


-(void)setImage:(UIImage *)image {
    _image = image;
    
    //添加圖片添加到數組當中
    [self.allPathArray addObject:image];
    //重繪
    [self setNeedsDisplay];
}

/**
 *    3:方法的封裝,一般我們在控制器寫代碼的時候,遇到的業務邏輯要想到去封裝,盡量減少控制器的代碼,封裝的代碼和誰有關系就封裝到哪里:1:能分類封裝就分類封裝,減少類的創建,節省空間,一個參數則可以直接選類方法,兩個參數建議使用對象方法,在分類中屬性定義某個變量,不會生成下划線的成員變量,需要自己重寫set'方法和get方法,才能獲得或是修改變量,若是在普通類中用readonly修飾,則在本類中可以調用get方法,但是不能使用set方法,可以使用下划線的成員變量,但是子類繼承父類后,不能繼承帶下划線的成員變量,不能調用set,只能調用get 2:封裝成工具類:單例或是類方法,若是單例,則盡量將方法抽成屬性,通過重寫封裝 ,類方法,想獲得單一一樣的成員變量則可以static定義全局或是局部變量。3:繼承:把相同的部分都封裝在父類中,不同的部分讓子類去單獨實現,若是父類想和子類進行數據溝通,則父類可以提供方法,供子類去重寫 4:三種方法封裝的主要思想就是:將與外界無關的邏輯全部封裝在類的內部,類在提供接口供外界訪問,讓外界調用是最簡潔的
 *
 */




/**
 *清屏:清屏就是要移除所有的路徑,此時刪除大數組中的所有的路徑就可以,在調用setNeedsDisplay,進行重繪,此時數組中沒有了任何一條路徑,所以就會清空上下文
 */
//清屏
- (void)clear {
    //清空所有的路徑
    [self.allPathArray removeAllObjects];
    //重繪
    [self setNeedsDisplay];
    
}

/**
 *撤銷:即是取出路徑數組中的最后一個路徑刪除,並調用setNeedsDisplay
 */
//撤銷
- (void)undo {
    //刪除最后一個路徑
    [self.allPathArray removeLastObject];
    //重繪
    [self setNeedsDisplay];
}

/**
 *    橡皮擦功能就是:又繪制了一條路徑,只是設置路徑的顏色為白色,將其他顏色的路徑覆蓋掉
 */
//橡皮擦
- (void)erase {
    [self setLineColor:[UIColor whiteColor]];
}



//設置線的寬度
- (void)setLineWith:(CGFloat)lineWidth {
    self.width = lineWidth;
}

//設置線的顏色
/**
 *    設置線的顏色,應該考慮到當沒有設置顏色的時候,或傳入的參數為空值的時候,所以要考慮以上兩點,所以要設置線的默認顏色,一次性設置,在init或是awakefromNib中去設置
 *
 *    @param color
 */
- (void)setLineColor:(UIColor *)color {
    self.color = color;
}


#pragma mark -- drawview的pan拖拽手勢,畫線
/**
 *2:在拖拽手勢方法中:1:繪制UIBezierPath路徑:開始設置起點,change的時候添加聯系,並調用setNeedsDisplay,異步調用drawRect方法  2:定義一個全局數組,用於保存所有的路徑,最后需要遍歷數組,將所有路徑取出來,繪制到上下文中 3:只有在自定義view中才能重寫drawRect方法,且drawRect方法配合setNeedsDisplay使用,此方法必須由系統調用才會生成與view相關聯的上下文,其中路徑可以在其他方法中繪制,但是最后將路徑繪制到上下文中的時候就必須在drawRect方法中實現[phath stroke];或是[path fill];
 
    2:什么情況下自定義類或是控件:1:當發現系統原始的功能,沒有辦法瞞足自己需求時,這個時候,要自定義類.繼承系統原來的東西.再去添加屬性自己的東西. 2:在begin方法中每次都創建一個全新的路徑,因為在一次繪制的時候begin方法只執行一次,將每一次創建的路徑都保存在大數組中,在drawrect中遍歷,得到路徑去繪制。其中顏色的繪制必須在drawrect上下文中繪制,否則不會顯示,因為路徑path沒有保存color,但是線寬有保存,所以自定義類MyBezierPath繼承UIBezierPath,提供color屬性,就是為了保存color,在draw遍歷時取出path后,直接設置路徑顏色。
 */
- (void)pan:(UIPanGestureRecognizer *)pan {
    
    //獲取的當前手指的點
    CGPoint curP = [pan locationInView:self];
    //判斷手勢的狀態
    if(pan.state == UIGestureRecognizerStateBegan) {
        //創建路徑
        //UIBezierPath *path = [UIBezierPath bezierPath];
        MyBezierPath *path = [[MyBezierPath alloc] init];
        self.path = path;
        //設置起點
        [path moveToPoint:curP];
        
        //設置線的寬度
        [path setLineWidth:self.width];
        //設置線的顏色
        //什么情況下自定義類:當發現系統原始的功能,沒有辦法瞞足自己需求時,這個時候,要自定義類.繼承系統原來的東西.再去添加屬性自己的東西.
        path.color = self.color;
   
        [self.allPathArray addObject:path];
        
    } else if(pan.state == UIGestureRecognizerStateChanged) {
        
        //繪制一根線到當前手指所在的點
        [self.path addLineToPoint:curP];
        //重繪
        [self setNeedsDisplay];
    }
    
}
/**
 * 1:當遍歷的時候,若是數組中含有的不只是同一種類型的對象,在遍歷的時候可以每個對象指定同一個類型的對象,再根據iskindofclass來判斷對象具體是那種類型。
   2:當畫圖片的時候:直接用image調用[image drawInRect:rect];或是drawpoint
 *
 */
-(void)drawRect:(CGRect)rect {
    
    //繪制保存的所有路徑
    for (MyBezierPath *path in self.allPathArray) {
        //判斷取出的路徑真實類型
        if([path isKindOfClass:[UIImage class]]) {
            UIImage *image = (UIImage *)path;
            [image drawInRect:rect];
        }else {
            [path.color set];
            [path stroke];
        }
    }
}

@end
#import <UIKit/UIKit.h>

@interface MyBezierPath : UIBezierPath

/** 當前路徑的顏色 */
@property (nonatomic, strong) UIColor *color;

@end
#import "MyBezierPath.h"

@implementation MyBezierPath

@end
#import <UIKit/UIKit.h>

@class HandleImageView;
@protocol handleImageViewDelegate <NSObject>

- (void)handleImageView:(HandleImageView *)handleImageView newImage:(UIImage *)newImage;

@end


@interface HandleImageView : UIView

/** <#注釋#> */
@property (nonatomic, strong) UIImage *image;

/** <#注釋#> */
@property (nonatomic, weak) id<handleImageViewDelegate> delegate;

@end
#import "HandleImageView.h"

@interface HandleImageView()<UIGestureRecognizerDelegate>

/** 在UIView上添加一張 UIImageView */
@property (nonatomic, weak) UIImageView *imageV;

@end

@implementation HandleImageView

/**
 *1: 懶加載UIImageView,屬性修飾也可以用weak修飾,能用weak的時候盡量用weak,其中_imageV = imageV賦值的時候既可以寫在添加[self addSubview:imageV];之前也可以寫在其之后
 *
 *    @return UIImageView
 */
-(UIImageView *)imageV {
    
    if (_imageV == nil) {
        UIImageView *imageV = [[UIImageView alloc] init];
        imageV.frame = self.bounds;
        imageV.userInteractionEnabled = YES;
        [self addSubview:imageV];
        _imageV = imageV;
        //添加手勢
        [self addGes];
    }
    return _imageV;
}

-(void)setImage:(UIImage *)image {
    _image = image;
    
    NSLog(@"%@",self.imageV);
    self.imageV.image = image;
    
}


/**
 * 2:添加手勢:1:添加了拖拽pan,長按longpress,捏合手勢pinch,旋轉手勢:rotation.1:這些手勢都分三種狀態,開始,改變,結束,其中在使用這些手勢一直繪制的時候,開始只調用一次 2:在這些手勢中都可以獲得觸摸點,locationInView,還可以拖拽距離translationInView,點擊的view,旋轉角度,捏合比例,而且若是想相對上次改變,則一定要進行復位操作  3:若是想同時支持多個手勢,需要將添加的手勢設置手勢代理,實現otherGestureRecognizer代理方法返回YES,則這樣就可以同時支持多個手勢。一般涉及旋轉平移縮放都與transform一起用,累加形變和非累加形變。 4:復位操作:復位,只要想相對於上一次旋轉就復位 [pan setTranslation:CGPointZero inView:pan.view]; pinch.scale = 1;rotation.rotation = 0;
 *
 */
//添加手勢
-(void)addGes{
    
    // pan
    // 拖拽手勢
    UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc]
                                   initWithTarget:self action:@selector(pan:)];
    
    [self.imageV addGestureRecognizer:pan];
    
    // pinch
    // 捏合
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(pinch:)];
    
    pinch.delegate = self;
    [self.imageV addGestureRecognizer:pinch];
    
    
    //添加旋轉
    UIRotationGestureRecognizer *rotation = [[UIRotationGestureRecognizer alloc] initWithTarget:self action:@selector(rotation:)];
    rotation.delegate = self;
    
    [self.imageV addGestureRecognizer:rotation];
    
    // 長按手勢
    UILongPressGestureRecognizer *longPress = [[UILongPressGestureRecognizer alloc] initWithTarget:self action:@selector(longPress:)];
    [self.imageV addGestureRecognizer:longPress];
    
}




//捏合的時候調用.
- (void)pinch:(UIPinchGestureRecognizer *)pinch
{
    
    pinch.view.transform = CGAffineTransformScale( pinch.view.transform, pinch.scale, pinch.scale);
    // 復位
    pinch.scale = 1;
}


//旋轉的時候調用
- (void)rotation:(UIRotationGestureRecognizer *)rotation
{
    // 旋轉圖片
    rotation.view.transform = CGAffineTransformRotate(rotation.view.transform, rotation.rotation);
    
    // 復位,只要想相對於上一次旋轉就復位
    rotation.rotation = 0;
    
}


//長按的時候調用
// 什么時候調用:長按的時候調用,而且只要手指不離開,拖動的時候會一直調用,手指抬起的時候也會調用
- (void)longPress:(UILongPressGestureRecognizer *)longPress
{
    
    if (longPress.state == UIGestureRecognizerStateBegan) {
        
        [UIView animateWithDuration:0.25 animations:^{
            //設置為透明
            self.imageV.alpha = 0;
        }completion:^(BOOL finished) {
            [UIView animateWithDuration:0.25 animations:^{
                self.imageV.alpha = 1;
                
                //把當前的View做一個截屏
                UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0);
                //獲取上下文
                CGContextRef ctx = UIGraphicsGetCurrentContext();
                [self.layer renderInContext:ctx];
                UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
                //關閉上下文.
                UIGraphicsEndImageContext();
   
                //調用代理方法
                if([self.delegate respondsToSelector:@selector(handleImageView:newImage:)]) {
                    
                    [self.delegate handleImageView:self newImage:newImage];
                }
                
                //從父控件當中移除
                [self removeFromSuperview];
                
            }];
        }];
        
        
    }
    
}

//拖動的時候調用
- (void)pan:(UIPanGestureRecognizer *)pan{
    
    CGPoint transP = [pan translationInView:pan.view];
    
    pan.view.transform = CGAffineTransformTranslate(pan.view.transform, transP.x, transP.y);
    //復位
    [pan setTranslation:CGPointZero inView:pan.view];
    
    
}


//能夠同時支持多個手勢
-(BOOL)gestureRecognizer:(nonnull UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(nonnull UIGestureRecognizer *)otherGestureRecognizer{
    
    return YES;
}
@end

 

畫板界面分析.

頂部是一個工具欄.有清屏,撤銷,橡皮擦,照片功能.最右部是一個保存按鈕

中間部分為畫板區域

最下部拖動滑竿能夠改變畫筆的粗線.可以選顏色.

 

1.界面搭建

最上部為一個ToolBar,往ToolBar拖些item,使用ToolBar的好處.里面按鈕的位置不需要我們再去管理.

給最上部的工具欄做自動布局.離父控件左,上,右都為0,保存工具條的高度不度

 

拖一個UIView當前下部的View.在下部的View當中拖累三個按鈕,設置每一個按鈕的背景顏色.

點擊每一按鈕時辦到設置畫筆的顏色.

其中三個按鈕只間的間距始終保存等,每一個按鈕的寬度和高度都相等.通過自動布局的方式辦到.

 

先把這個UIView的自動布局設好, 讓其左,右,下都是0,高度固定.

 

自動布局設置為:第一個按鈕高度固定,與左,右,下都保存20個間距.

第二個按鈕與第一個按鈕,高度,寬度,centerY都相等.與右邊有20個間距.

第三個按鈕也是第一個按鈕的高度,寬度,centerY都相等.與右邊有20個間距,最右邊也保存20個間距.

 

最后是中間的畫板區域,畫板區域只需要上距離上下左右都為0即可.

 

2.實現畫板功能.

 

  當手指移動的時候,在中間的畫板區域當中進行繪制.由於每一個路徑都有不同的狀態.所以我們就不能用一條路徑來做.

  所以要弄一個數組記錄住每一條路徑.

  實現畫板功能.

  1.監聽手指在屏幕上的狀態.在開始點擊屏幕的時候,創建一個路徑.並把手指當前的點設為路徑的起點.

 

    弄一個成員屬性記錄當前繪制的路徑.並把當前的路徑添加到路徑數組當中.

   

  2.當手指在移動的時候,用當前的路徑添加一根線到當前手指所在的點.然后進行重繪.

  3.在繪圖方法當中.取出所有的路徑.把所有的路徑給繪制出來.

 

3.設置路徑屬性.

提供屬性方法.

 

清屏功能:刪除所有路徑進行重繪

 

撤銷功能:刪除最后一條路徑,進行重繪

 

設置線寬:

由於每一條線寬度都不樣.所以要在開始創建路徑的時,就要添加一個成員屬性,設置一個默認值.

在把當前路徑添加到路徑數組之前,設置好線的寬度.然后重寫線寬屬性方法.

下一次再創建路徑時,線的寬度就是當前設置的寬度.

設置線的顏色:

同樣,由於每一條線的顏色也不一樣.也需要記錄住每一條路徑的顏色.

由於UIBezierPath沒有給我們直接提供設置顏色的屬性.我們可以自定義一個UIBezierPath.

創建一個MyBezierPath類,繼承UIBezierPath,在該類中添加一個顏色的屬性.

在創建路徑的時候,直接使用自己定義的路徑.設置路徑默認的一個顏色.方法給設置線寬一樣.

在繪圖過程中, 取出來的都是MyBezierPath,把MyBezierPath的顏色設置路徑的顏色.

 

橡皮擦功能:橡皮擦功能其實就是把路徑的顏色設為白色.

 

 

4.保存繪制的圖片到相冊.

保存相冊的思路:就是把繪制的在View上的內容生成一張圖片,保存到系統相冊當中.

具體步驟:

開啟一個跟View相同大小的圖片上下文.

把View的layer上面內容渲染到上下文當中.

生成一張圖片,把圖片保存到上下文.

關閉上下文.

如何把一張圖片保存到上下文?

調用方法:

參數說明:

第一個參數:要寫入到相冊的圖片.

第二個參數:哪個對象堅聽寫入完成時的狀態.

第三個參數:圖片保存完成時調用的方法

UIImageWriteToSavedPhotosAlbum(newImage,

  self,

@selector(image:didFinishSavingWithError: contextInfo:),nil);

注意:圖片保存完成時調用的方法必須得是image:didFinishSavingWithError: contextInfo:

 

 

5.選擇圖片.

點擊圖片時彈出系統的相冊.

如果彈出系統的相冊?

使用UIImagePickerController控件器Modal出它來.

UIImagePickerController *pick = [[UIImagePickerController alloc] init];

 

設置照片的來源

  pick.sourceType =  UIImagePickerControllerSourceTypeSavedPhotosAlbum;

 

  設置代碼,監聽選擇圖片,UIImagePickerController比較特殊,它需要遵守兩個協議

  <UINavigationControllerDelegate,UIImagePickerControllerDelegate>

  pick.delegate = self;

 

  modal出控件器

  [self presentViewController:pick animated:YES completion:nil];

 

  注意沒有實現代碼方法時,選擇一張照片會自動的dismiss掉相冊控制器.但是設置代碼后,就得要自己去dismiss了

 

 

  實現代理方法.

  選擇的照片就在這個方法第二個參數當中, 它是一個字典

  -(void)imagePickerController:(nonnull UIImagePickerController *)picker

  didFinishPickingMediaWithInfo:(nonnull NSDictionary<NSString *,id> *)info{

 

獲取當前選中的圖片.通過UIImagePickerControllerOriginalImage就能獲取.

UIImage *image = info[UIImagePickerControllerOriginalImage];

 

}

 

獲取完圖片后.圖片是能夠縮放,平移的,因此獲取完圖片后,是在畫板板View上面添加了一個UIView.

只有UIView才能做平移,縮放,旋轉等操作.

因此做法為.在圖片選擇完畢后,添加一個和畫板View相同大小的UIView,這個UIView內部有一個UIImageView.

對這個UIImageView進行一些手勢操作.操作完成時.長按圖片,把View的內容截屏,生成一張圖片.

把新生成的圖片繪制到畫板上面.

 

6.繪制圖片.

在畫板View當中提供一個UImage屬性,供外界傳遞.重寫屬性的set方法,每次傳遞圖片時進行重繪

畫圖片也有有序的,所以要把圖片也添加到路徑數組當中.

在繪圖片過過程當中,如果發現取出來的是一個圖片類型.那就直接圖片繪制到上下文當中.

 

具體實現代碼為:

-(void)drawRect:(CGRect)rect{

 

    for (DrawPath *path in self.pathArray) {

        

        if ([path isKindOfClass:[UIImage class]]) {

            

         UIImage *image = (UIImage *)path;

            [image drawInRect:rect];

 

        }else{

            [path.lineColor set];

            [path stroke];

        }

        

    }

    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM