iOS开发之画图板(贝塞尔曲线)

简介:

 贝塞尔曲线,听着挺牛气一词,不过下面我们在做画图板的时候就用到贝塞尔绘直线,没用到绘制曲线的功能。如果会点PS的小伙伴会对贝塞尔曲线有更直观的理解。这篇博文的重点不在于如何用使用贝塞尔曲线,而是利用贝塞尔划线的功能来封装一个画图板。

  画图板的截图如下,上面的白板就是我们的画图板,是自己封装好的一个UIView,下面会详细的介绍如何封装这个画图板,下面的控件用来控制我们画图板的属性以及Undo,Redo和保存功能。点击保存时会把绘制的图片保存到手机的相册中。下面是具体的实现方案。

 

  一.封装画图板

    其实上面的白板就是一继承于UiView的一个子类,我们可以在这个子类中添加我们画图板相应的属性和方法,然后实例化成对象添加到ViewController中,当然为了省事添加白板的时候是通过storyboard来完成的,读者也可以自己实例化然后手动的添加到相应的ViewController中。

    1.封装白板的第一步是新建一个UIView的子类MyView,然后添加相应的属性和方法。MyView.h中的代码如下,代码具体意思请参照注释

#import <UIKit/UIKit.h>

@interface MyView : UIView
//用来设置线条的颜色
@property (nonatomic, strong) UIColor *color;
//用来设置线条的宽度
@property (nonatomic, assign) CGFloat lineWidth;
//用来记录已有线条
@property (nonatomic, strong) NSMutableArray *allLine;

//初始化相关参数
-(void)initMyView;
//unDo操作
-(void)backImage;
//reDo操作
-(void)forwardImage;

@end

 

    2、上面的代码是对外的接口,有些属性我们是写在MyView.m的延展中以实现私有的目的,MyView延展部分如下:

@interface MyView()
//声明贝塞尔曲线
@property(nonatomic, strong) UIBezierPath *bezier;
//存储Undo出来的线条
@property(nonatomic, strong) NSMutableArray *cancleArray;
@end

 

    3.下面的代码就是实现部分的代码了,会根据不同功能给出相应的说明

      (1).初始化我们的白板,给线条指定默认颜色和宽度并且给相应的变量分配内存空间,初始化代码如下:

//进行一些初始化工作
-(void)initMyView
{
    self.color = [UIColor redColor];
    self.lineWidth = 1;
    self.allLine = [NSMutableArray arrayWithCapacity:50];
    self.cancleArray = [NSMutableArray arrayWithCapacity:50];
}

  

      (2)Undo功能的封装,相当于两个栈,把显示的线条出栈,进入为不显示的线条栈中,每执行一次此操作显示线条栈中的元素会少一条而不显示线条栈中会多一条,大致就这个意思吧,代码如下:

//UnDo操作
-(void)backImage
{
    if (self.allLine.count > 0)
    {
        int index = self.allLine.count - 1;
        
        [self.cancleArray addObject:self.allLine[index]];
        
        [self.allLine removeObjectAtIndex:index];
        
        [self setNeedsDisplay
         ];
    }
}

    

      (3)Redo操作和Undo操作相反,从未显示栈中取出元素放入显示的栈中,代码中的栈我们是用数组来表示的,代码如下:

//ReDo操作
-(void)forwardImage
{
    if (self.cancleArray.count > 0)
    {
        int index = self.cancleArray.count - 1;
        
        [self.allLine addObject:self.cancleArray[index]];
    
        [self.cancleArray removeObjectAtIndex:index];
        
        [self setNeedsDisplay];
    }
}


      (4)、当开始触摸时我们新建一个BezierPath,把触摸起点设置成BezierPath的起点,并把将要画出的线条以及线条对应的属性封装成字典添加到显示栈中,代码如下
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
    //新建贝塞斯曲线
    self.bezier = [UIBezierPath bezierPath];
    
    //获取触摸的点
    UITouch *myTouche = [touches anyObject];
    CGPoint point = [myTouche locationInView:self];
    
    //把刚触摸的点设置为bezier的起点
    [self.bezier moveToPoint:point];
    
    //把每条线存入字典中
    NSMutableDictionary *tempDic = [[NSMutableDictionary alloc] initWithCapacity:3];
    [tempDic setObject:self.color forKey:@"color"];
    [tempDic setObject:[NSNumber numberWithFloat:self.lineWidth] forKey:@"lineWidth"];
    [tempDic setObject:self.bezier forKey:@"line"];
    
    //把线加入数组中
    [self.allLine addObject:tempDic];

}

 

      (5)当移动也就是划线的时候把点存储到BezierPath中,代码如下

-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
    UITouch *myTouche = [touches anyObject];
    CGPoint point = [myTouche locationInView:self];
    
    [self.bezier addLineToPoint:point];
    
    //重绘界面
    [self setNeedsDisplay];
    
}


      (6)画出线条
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
    //对之前的线的一个重绘过程
    for (int i = 0; i < self.allLine.count; i ++)
    {
        NSDictionary *tempDic = self.allLine[i];
        UIColor *color = tempDic[@"color"];
        CGFloat width = [tempDic[@"lineWidth"] floatValue];
        UIBezierPath *path = tempDic[@"line"];
        
        [color setStroke];
        [path setLineWidth:width];
        [path stroke];
    }

}

  二.画图板的使用

    上面是封装画图板要用到的全部代码,下面的代码就是如何在ViewController中使用我们的画图板了,如何实例化控件,以及控件的初始化,注册回调等在这就不做赘述了,下面给出了主要控件的回调方法

    1、通过Slider来调节线条的宽度

1 //通过slider来设置线条的宽度
2 - (IBAction)sliderChange:(id)sender
3 {
4     self.myView.lineWidth = self.mySlider.value;
5 }

  

    2、通过SegmentControl来设置线条的颜色

/通过segmentControl来设置线条的颜色
- (IBAction)tapSegment:(id)sender {
    
    switch (self.mySegment.selectedSegmentIndex) {
        case 0:
            self.myView.color = [UIColor redColor];
            break;
        case 1:
            self.myView.color = [UIColor blackColor];
            break;
        case 2:
            self.myView.color = [UIColor greenColor];
            break;
            
        default:
            break;
    }
    
}


    3、undo和redo操作
//Undo
- (IBAction)tapBack:(id)sender {
    [self.myView backImage];
}


//Redo操作
- (IBAction)tapGo:(id)sender {
    [self.myView forwardImage];
}

 

    4.保存操作,也许下面的保存操作在处理方式上略显笨拙,如有更好的解决方案请留言。 保存的时候我是先截了个屏,然后把白板进行切割,把切割后图片存入到相册中,代码如下:

//把画的图保存到相册
- (IBAction)tapSave:(id)sender {
    //截屏
    UIGraphicsBeginImageContext(self.view.bounds.size);
    [self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
    UIImage *uiImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
    
    
    //截取画图版部分
    CGImageRef sourceImageRef = [uiImage CGImage];
    CGImageRef newImageRef = CGImageCreateWithImageInRect(sourceImageRef, CGRectMake(36, 6, 249, 352));
    UIImage *newImage = [UIImage imageWithCGImage:newImageRef];
    
    //把截的屏保存到相册
    UIImageWriteToSavedPhotosAlbum(newImage , nil, nil, nil);
    
    //给个保存成功的反馈
    UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"存储照片成功"
                                                    message:@"您已将照片存储于图片库中,打开照片程序即可查看。"
                                                   delegate:self
                                          cancelButtonTitle:@"OK"
                                          otherButtonTitles:nil];
    [alert show];

}


以上就是本画图板的主要代码了,有不足之处还望批评指正。转载请注明出处。在本文结束时在来几张截图吧(demo下载地址:http://www.pgyer.com/LTQ8):

 

相关文章
|
22天前
|
Java Android开发 Swift
安卓与iOS开发对比:平台选择对项目成功的影响
【10月更文挑战第4天】在移动应用开发的世界中,选择合适的平台是至关重要的。本文将深入探讨安卓和iOS两大主流平台的开发环境、用户基础、市场份额和开发成本等方面的差异,并分析这些差异如何影响项目的最终成果。通过比较这两个平台的优势与挑战,开发者可以更好地决定哪个平台更适合他们的项目需求。
84 1
|
29天前
|
设计模式 安全 Swift
探索iOS开发:打造你的第一个天气应用
【9月更文挑战第36天】在这篇文章中,我们将一起踏上iOS开发的旅程,从零开始构建一个简单的天气应用。文章将通过通俗易懂的语言,引导你理解iOS开发的基本概念,掌握Swift语言的核心语法,并逐步实现一个具有实际功能的天气应用。我们将遵循“学中做,做中学”的原则,让理论知识和实践操作紧密结合,确保学习过程既高效又有趣。无论你是编程新手还是希望拓展技能的开发者,这篇文章都将为你打开一扇通往iOS开发世界的大门。
|
30天前
|
搜索推荐 IDE API
打造个性化天气应用:iOS开发之旅
【9月更文挑战第35天】在这篇文章中,我们将一起踏上iOS开发的旅程,通过创建一个个性化的天气应用来探索Swift编程语言的魅力和iOS平台的强大功能。无论你是编程新手还是希望扩展你的技能集,这个项目都将为你提供实战经验,帮助你理解从构思到实现一个应用的全过程。让我们开始吧,构建你自己的天气应用,探索更多可能!
52 1
|
2月前
|
IDE Android开发 iOS开发
探索Android与iOS开发的差异:平台选择对项目成功的影响
【9月更文挑战第27天】在移动应用开发的世界中,Android和iOS是两个主要的操作系统平台。每个系统都有其独特的开发环境、工具和用户群体。本文将深入探讨这两个平台的关键差异点,并分析这些差异如何影响应用的性能、用户体验和最终的市场表现。通过对比分析,我们将揭示选择正确的开发平台对于确保项目成功的重要作用。
|
2月前
|
开发框架 数据可视化 Java
iOS开发-SwiftUI简介
iOS开发-SwiftUI简介
|
3天前
|
安全 API Swift
探索iOS开发中的Swift语言之美
【10月更文挑战第23天】在数字时代的浪潮中,iOS开发如同一艘航船,而Swift语言则是推动这艘船前进的风帆。本文将带你领略Swift的独特魅力,从语法到设计哲学,再到实际应用案例,我们将一步步深入这个现代编程语言的世界。你将发现,Swift不仅仅是一种编程语言,它是苹果生态系统中的一个创新工具,它让iOS开发变得更加高效、安全和有趣。让我们一起启航,探索Swift的奥秘,感受编程的乐趣。
|
5天前
|
Swift iOS开发 开发者
探索iOS开发中的SwiftUI框架
【10月更文挑战第21天】在苹果生态系统中,SwiftUI的引入无疑为iOS应用开发带来了革命性的变化。本文将通过深入浅出的方式,带领读者了解SwiftUI的基本概念、核心优势以及如何在实际项目中运用这一框架。我们将从一个简单的例子开始,逐步深入到更复杂的应用场景,让初学者能够快速上手,同时也为有经验的开发者提供一些深度使用的技巧和策略。
19 1
|
22天前
|
移动开发 前端开发 Swift
iOS 最好的应用程序开发编程语言竟然是这7种
iOS 最好的应用程序开发编程语言竟然是这7种
63 8
|
21天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异:从代码到用户体验
【10月更文挑战第5天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。它们在技术架构、开发环境及用户体验上有着根本的不同。本文通过比较这两种平台的开发过程,揭示背后的设计理念和技术选择如何影响最终产品。我们将深入探讨各自平台的代码示例,理解开发者面临的挑战,以及这些差异如何塑造用户的日常体验。
|
25天前
|
安全 Swift iOS开发
探索iOS开发中的Swift语言之美
在数字时代的浪潮中,移动应用已成为日常生活的延伸。本文将深入探讨iOS平台上的Swift编程语言,揭示其背后的设计哲学、语法特性以及如何利用Swift进行高效开发。我们将通过实际代码示例,展示Swift语言的强大功能和优雅简洁的编程风格,引导读者理解并运用Swift解决实际问题。