iOS开发 教学白板,互动版本,画画功能实现(类似于你画我猜功能)

本文分享了iOS平台实时连接画画功能的开发经验,包括利用socket服务器实现数据实时传输,使用CAShapeLayer和UIBezierPath进行屏幕绘图,以及将绘图点转化为二进制数据发送的技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值