iOS小夥伴們有時候是不是會遇到開發時需要實現實時連接的畫畫功能呢? 類似於你畫我猜!這個開發功能可以用於直播軟件和在線教育軟件裏面
剛好我公司項目最近有這個功能,今天剛開發完,跟大家分享一下心得和方法,首先這個功能的實現其實就三個步驟
1:公司擁有自己的socket服務器,這是基礎,實時鏈接數據
2:畫畫功能,畫畫功能其實就是把自己在屏幕視圖上滑過的地方的點記錄起來,根據CAShapeLayer和UIBezierPath進行描點畫畫
3:最後就是數據傳輸,需要我們在每次滑動的時候就把手指經歷過的地方的點發送給對方,這就需要把點加載進數組裏面,然後把數組轉化爲二進制數據發送給對方,具體要求看產品需求
最後直接上代碼和效果圖,有需要的小夥伴可以互相探討哦
繪畫代碼:
//
// WhiteBoardDrawView.h
// NIM
//
// Created by 趙發生 on 15/7/1.
// Copyright (c) 2015年 Netease. All rights reserved.
//
#import <UIKit/UIKit.h>
@interface NTESWhiteboardDrawView : UIView
- (void)setLineColor:(UIColor *)color;
- (void)setLineWidth:(CGFloat)width;
- (void)addPoints:(NSMutableArray *)points isNewLine:(BOOL)newLine;
- (void)deleteLastLine;
- (void)clear;
@end
#import "NTESWhiteboardDrawView.h"
#import <QuartzCore/QuartzCore.h>
#import "NTESCADisplayLinkHolder.h"
@interface NTESWhiteboardDrawView()<NTESCADisplayLinkHolderDelegate>
@property(nonatomic, strong)NSMutableArray *myLines;
@property(nonatomic, assign)BOOL shouldDraw;
@property(nonatomic, strong)NTESCADisplayLinkHolder *displayLinkHolder;
@end
@implementation NTESWhiteboardDrawView
#pragma mark - public methods
- (id)initWithFrame:(CGRect)frame
{
if (self = [super initWithFrame:frame]) {
_myLines = [[NSMutableArray alloc] init];
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
shapeLayer.strokeColor = [UIColor blackColor].CGColor;
shapeLayer.fillColor = [UIColor clearColor].CGColor;
shapeLayer.lineJoin = kCALineJoinRound;
shapeLayer.lineCap = kCALineCapRound;
shapeLayer.lineWidth = 2;
shapeLayer.masksToBounds = YES;
_displayLinkHolder = [[NTESCADisplayLinkHolder alloc] init];
[_displayLinkHolder setFrameInterval:3];
[_displayLinkHolder startCADisplayLinkWithDelegate:self];
}
return self;
}
-(void)dealloc
{
[_displayLinkHolder stop];
}
+ (Class)layerClass
{
return [CAShapeLayer class];
}
- (void)setLineColor:(UIColor *)color
{
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
shapeLayer.strokeColor = color.CGColor;
}
- (void)setLineWidth:(CGFloat)width{
CAShapeLayer *shapeLayer = (CAShapeLayer *)self.layer;
shapeLayer.lineWidth = width;
}
- (void)addPoints:(NSMutableArray *)points isNewLine:(BOOL)newLine
{
__weak typeof(self) wself = self;
dispatch_async(dispatch_get_main_queue(), ^{
[wself addPoints:points toLines:[wself myLines] isNewLine:newLine];
});
}
- (void)deleteLastLine
{
__weak typeof(self) wself = self;
dispatch_async(dispatch_get_main_queue(), ^{
[wself deleteLastLine:[wself myLines]];
});
}
- (void)deleteAllLines
{
__weak typeof(self) wself = self;
dispatch_async(dispatch_get_main_queue(), ^{
[wself deleteAllLines:[wself myLines]];
});
}
- (void)clear
{
[self deleteAllLines];
}
#pragma mark - private methods
- (void)addPoints:(NSMutableArray *)points
toLines:(NSMutableArray *)lines
isNewLine:(BOOL)newLine
{
if (newLine) {
[lines addObject:points];
}
else if (lines.count == 0) {
[lines addObject:points];
}
else {
NSMutableArray *lastLine = [lines lastObject];
[lastLine addObjectsFromArray:points];
}
_shouldDraw = YES;
}
-(void)deleteLastLine:(NSMutableArray *)lines
{
[lines removeLastObject];
_shouldDraw = YES;
}
-(void)deleteAllLines:(NSMutableArray *)lines
{
[lines removeAllObjects];
_shouldDraw = YES;
}
- (void)onDisplayLinkFire:(NTESCADisplayLinkHolder *)holder
duration:(NSTimeInterval)duration
displayLink:(CADisplayLink *)displayLink
{
if (!_shouldDraw) {
return;
}
UIBezierPath *path = [[UIBezierPath alloc] init];
NSUInteger linesCount = _myLines.count;
for (NSUInteger i = 0 ; i < linesCount; i ++) {
NSArray *line = [_myLines objectAtIndex:i];
NSUInteger pointsCount = line.count;
for (NSUInteger j = 0 ; j < pointsCount; j ++) {
NSArray *point = [line objectAtIndex:j];
CGPoint p = CGPointMake([point[0] floatValue], [point[1] floatValue]);
if (j == 0) {
[path moveToPoint:p];
}
else {
[path addLineToPoint:p];
}
}
}
((CAShapeLayer *)self.layer).path = path.CGPath;
_shouldDraw = NO;
}
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
@class NTESCADisplayLinkHolder;
@protocol NTESCADisplayLinkHolderDelegate <NSObject>
- (void)onDisplayLinkFire:(NTESCADisplayLinkHolder *)holder
duration:(NSTimeInterval)duration
displayLink:(CADisplayLink *)displayLink;
@end
@interface NTESCADisplayLinkHolder : NSObject
@property (nonatomic,weak ) id<NTESCADisplayLinkHolderDelegate> delegate;
@property (nonatomic,assign) NSInteger frameInterval;
- (void)startCADisplayLinkWithDelegate: (id<NTESCADisplayLinkHolderDelegate>)delegate;
- (void)stop;
@end
#import "NTESCADisplayLinkHolder.h"
@implementation NTESCADisplayLinkHolder
{
CADisplayLink *_displayLink;
}
- (instancetype)init
{
if (self = [super init]) {
_frameInterval = 1;
}
return self;
}
- (void)dealloc
{
[self stop];
_delegate = nil;
}
- (void)startCADisplayLinkWithDelegate:(id<NTESCADisplayLinkHolderDelegate>)delegate
{
_delegate = delegate;
[self stop];
_displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(onDisplayLink:)];
[_displayLink setFrameInterval:_frameInterval];
[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];
}
- (void)stop
{
if (_displayLink){
[_displayLink invalidate];
_displayLink = nil;
}
}
- (void)onDisplayLink: (CADisplayLink *) displayLink
{
if (_delegate && [_delegate respondsToSelector:@selector(onDisplayLinkFire:duration:displayLink:)]){
[_delegate onDisplayLinkFire:self
duration:displayLink.duration
displayLink:displayLink];
}
}
畫畫例子:
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if (!self.isConnect) {
return;
}
if (!self.sessionId) {
return;
}
CGPoint p = [[touches anyObject] locationInView:self.callView.myDrawView];
[self onPointCollected:p type:WhiteBoardCmdTypePointEnd];
}
- (void)onPointCollected:(CGPoint)p type:(WhiteBoardCmdType)type
{
//send to peer
NSString *cmd = [NSString stringWithFormat:@"%zd:%.3f,%.3f;", type, p.x/self.callView.myDrawView.frame.size.width, p.y/self.callView.myDrawView.frame.size.height];
[self addCmd:cmd];
//local render
NSArray *point = [NSArray arrayWithObjects:@(p.x), @(p.y), nil];
[self.callView.myDrawView addPoints:[NSMutableArray arrayWithObjects:point, nil]
isNewLine:(type == WhiteBoardCmdTypePointStart)];
}
- (void)addCmd:(NSString *)aCmd
{
[_cmdsLock lock];
[_cmds appendString:aCmd];
[_cmdsLock unlock];
if ([_cmds length]) {
[self sendCmds];
}
}
hui