Sketch On UIView in UIScrollView

I am having a problem while creating a thumbnail for UIView

which is a child of the view in UIScrollView

.

The problem occurs when you try to sketch on UIView

after enlarging or scrolling. It seems my sketch code (shown below) doesn't account for scale or scale UIView

within UIScrollView

, because the lines are blurry and not showing where they should. Please let me know if there is something I can do with my code or provide another solution.

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

      NSLog(@"Draw touchesBegan");

      mouseSwipedPSVC = NO;
      UITouch *touch = [touches anyObject];
      lastPointPSVC = [touch locationInView:sheetDrawView];

}

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {

      mouseSwipedPSVC = YES;
      UITouch *touch = [touches anyObject];
      CGPoint currentPoint = [touch locationInView:sheetDrawView];

      UIGraphicsBeginImageContext(sheetDrawView.frame.size);
      [drawImageView.image drawInRect:CGRectMake(0, 0,       sheetDrawView.frame.size.width, sheetDrawView.frame.size.height)];
      CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPointPSVC.x, lastPointPSVC.y);
      CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), currentPoint.x, currentPoint.y);
      CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
      CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushPSVC);
      CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), redPSVC, greenPSVC, bluePSVC, 1.0);
      CGContextSetBlendMode(UIGraphicsGetCurrentContext(),kCGBlendModeNormal);

      CGContextStrokePath(UIGraphicsGetCurrentContext());
      drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
      drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
      [drawImageView setAlpha:opacityPSVC];
      UIGraphicsEndImageContext();

      lastPointPSVC = currentPoint;

}

- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {

     if(!mouseSwipedPSVC) {
        NSLog(@"Check 1");

        UIGraphicsBeginImageContext(sheetDrawView.frame.size);
        [drawImageView.image drawInRect:CGRectMake(0, 0, sheetDrawView.frame.size.width, sheetDrawView.frame.size.height)];
        CGContextSetLineCap(UIGraphicsGetCurrentContext(), kCGLineCapRound);
        CGContextSetLineWidth(UIGraphicsGetCurrentContext(), brushPSVC);
        CGContextSetRGBStrokeColor(UIGraphicsGetCurrentContext(), redPSVC, greenPSVC, bluePSVC, opacityPSVC);
        CGContextMoveToPoint(UIGraphicsGetCurrentContext(), lastPointPSVC.x, lastPointPSVC.y);
        CGContextAddLineToPoint(UIGraphicsGetCurrentContext(), lastPointPSVC.x, lastPointPSVC.y);
        CGContextStrokePath(UIGraphicsGetCurrentContext());
        CGContextFlush(UIGraphicsGetCurrentContext());
        drawImageView.image = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();

   }

}

      

I must add that this code works fine unless UIView

zoomed in or scrolled down. In addition, sheetDrawView is a child UIScrollView

.

0


source to share


1 answer


Created a small drawing application (tested only on iPhone 7 Plus 10.1 Simulator).

enter image description here

First, I created PalleteView. This view allows you to choose colors to paint with.

PaletteView.h:

//
//  PaletteView.h
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import <UIKit/UIKit.h>

@class PaletteView;

@protocol PaletteViewDelegate <NSObject>
- (void)didSelectColour:(PaletteView * _Nonnull)paletteView colour:(UIColor * _Nonnull)colour;
@end

@interface PaletteView : UIView
@property (nullable, nonatomic, weak) id<PaletteViewDelegate> delegate;
@end

      

PaletteView.m:

//
//  PaletteView.m
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import "PaletteView.h"

#define kPaletteViewCell @"kPaletteViewCell"

@interface PaletteView() <UICollectionViewDelegate, UICollectionViewDataSource>
@property (nonnull, nonatomic, strong) UICollectionView *collectionView;
@property (nonnull, nonatomic, strong) NSArray<NSArray<UIColor *> *> *colours;
@end

@implementation PaletteView
- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {
        [self initControls];
        [self setTheme];
        [self registerClasses];
        [self doLayout];
    }
    return self;
}

- (void)initControls {

    CGFloat idealWidth = (self.frame.size.width / 7.0) - (2.5 * 5.0);
    CGFloat idealHeight = (self.frame.size.height / 2.0) - (2.5 * 5.0);

    UICollectionViewFlowLayout *layout = [[UICollectionViewFlowLayout alloc] init];
    layout.minimumLineSpacing = 5.0;
    layout.minimumInteritemSpacing = 5.0;
    layout.sectionInset = UIEdgeInsetsMake(5.0, 5.0, 5.0, 5.0);
    layout.scrollDirection = UICollectionViewScrollDirectionVertical;
    layout.itemSize = CGSizeMake(idealWidth, idealHeight);

    self.collectionView = [[UICollectionView alloc] initWithFrame:CGRectZero collectionViewLayout:layout];

    self.colours = @[@[[UIColor blackColor],
                     [UIColor darkGrayColor],
                     [UIColor lightGrayColor],
                     [UIColor whiteColor],
                     [UIColor grayColor],
                     [UIColor redColor],
                     [UIColor greenColor]],

                     @[[UIColor blueColor],
                     [UIColor cyanColor],
                     [UIColor yellowColor],
                     [UIColor magentaColor],
                     [UIColor orangeColor],
                     [UIColor purpleColor],
                     [UIColor brownColor]]];
}

- (void)setTheme {
    [self.collectionView setDelegate:self];
    [self.collectionView setDataSource:self];
    [self.collectionView setAlwaysBounceHorizontal:YES];
    [self.collectionView setDelaysContentTouches:NO];
    [self.collectionView setShowsHorizontalScrollIndicator:NO];
    [self.collectionView setShowsVerticalScrollIndicator:NO];
    [self.collectionView setBackgroundColor:[UIColor colorWithRed:240.0/255.0 green:229.0/255.0 blue:227.0/255.0 alpha:1.0]];
}

- (void)registerClasses {
    [self.collectionView registerClass:[UICollectionViewCell class] forCellWithReuseIdentifier:kPaletteViewCell];
}

- (void)doLayout {
    [self addSubview:self.collectionView];

    [self.collectionView.leftAnchor constraintEqualToAnchor:self.leftAnchor].active = YES;
    [self.collectionView.rightAnchor constraintEqualToAnchor:self.rightAnchor].active = YES;
    [self.collectionView.topAnchor constraintEqualToAnchor:self.topAnchor].active = YES;
    [self.collectionView.bottomAnchor constraintEqualToAnchor:self.bottomAnchor].active = YES;

    [self.collectionView setTranslatesAutoresizingMaskIntoConstraints:NO];
}

- (NSInteger)numberOfSectionsInCollectionView:(UICollectionView *)collectionView {
    return self.colours.count; //Two rows of colours.
}

- (NSInteger)collectionView:(UICollectionView *)collectionView numberOfItemsInSection:(NSInteger)section {
    return [self.colours[section] count];  //7 colours per row in this example.
}

- (__kindof UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath {

    UICollectionViewCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:kPaletteViewCell forIndexPath:indexPath];

    NSArray *section = [self.colours objectAtIndex:indexPath.section];
    [cell.contentView setBackgroundColor:section[indexPath.row]];

    return cell;
}

- (void)collectionView:(UICollectionView *)collectionView didSelectItemAtIndexPath:(NSIndexPath *)indexPath {
    if ([self.delegate respondsToSelector:@selector(didSelectColour:colour:)]) {
        NSArray *section = [self.colours objectAtIndex:indexPath.section];
        [self.delegate didSelectColour:self colour:section[indexPath.row]];
    }
}
@end

      

This is a simple collectionView with each cell colored. I have hard-coded the cell sizes.

Then I created a DrawingView. This is the view that the user will use using their finger. This view only handles one finger in the temporary drawing. I first thought about painting to Bitmap from GameDesign. In games, you want to draw textures in memory first. Then, when you are done making all of your drawings, you will push that frame onto the screen. This greatly improves speed because you don't refresh the screen after EVERY operation. Instead, you refresh the screen at the end of the entire drawing (when the user lifts their finger).

To do this, I did the following:



DrawingView.h:

//
//  DrawingView.h
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface DrawingView : UIView
- (void)setPaletteColour:(UIColor * _Nonnull)colour;
@end

      

DrawingView.m:

//
//  DrawingView.m
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import "DrawingView.h"

@interface DrawingView()
@property (nonnull, nonatomic, strong) UIBezierPath *path;
@property (nonnull, nonatomic, strong) UIImage *bufferedImage;
@property (nonnull, nonatomic, strong) UIColor *strokeColour;
@end

@implementation DrawingView

- (instancetype)initWithFrame:(CGRect)frame {
    if ((self = [super initWithFrame:frame])) {

        [self setPath:[UIBezierPath bezierPath]];
        [self.path setLineWidth:1.0];

        [self setStrokeColour:[UIColor blackColor]];
        [self setMultipleTouchEnabled:NO];
    }
    return self;
}

- (void)setPaletteColour:(UIColor *)colour {
    self.strokeColour = colour;
}

- (void)renderToBufferedImage {
    UIGraphicsBeginImageContextWithOptions(self.bounds.size, NO, 0.0);

    [self.strokeColour setStroke];
    [self.bufferedImage drawAtPoint:CGPointZero];
    [self.path stroke];

    self.bufferedImage = UIGraphicsGetImageFromCurrentImageContext();
    UIGraphicsEndImageContext();
}



- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    [self.path moveToPoint:[touch locationInView:self]];
}

- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    [self.path addLineToPoint:[touch locationInView:self]];
    [self setNeedsDisplay];
}

- (void)touchesEnded:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    [self.path addLineToPoint:[touch locationInView:self]];
    [self renderToBufferedImage];
    [self setNeedsDisplay];
    [self.path removeAllPoints];
}

- (void)touchesCancelled:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    [self.path addLineToPoint:[touch locationInView:self]];
    [self renderToBufferedImage];
    [self setNeedsDisplay];
    [self.path removeAllPoints];
}

- (void)drawRect:(CGRect)rect {
    [self.strokeColour setStroke];
    [self.bufferedImage drawInRect:rect blendMode:kCGBlendModeNormal alpha:1.0];
    [self.path stroke];
}

@end

      

I decided to use drawInRect:blendMode:alpha

it because it will allow you to paint with different blending options and alpha levels. In this example, I am drawing fully opaque 32-bit BGRA bitmaps.

Then I created a controller with an embedded ScrollView. This allows the user to ZOOM-IN / Scale the view and draw the zoomed / scaled view. When you zoom in on the image, you will see that the drawing is accurate (not distorted or scaled).

ViewController.h:

//
//  ViewController.h
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController


@end

      

ViewController.m:

//
//  ViewController.m
//  DrawingIO
//
//  Created by Brandon T on 2016-11-27.
//  Copyright Β© 2016 XIO. All rights reserved.
//

#import "ViewController.h"
#import "PaletteView.h"
#import "DrawingView.h"

@interface ViewController () <UIScrollViewDelegate, PaletteViewDelegate>
@property (nonnull, nonatomic, strong) PaletteView *paletteView;
@property (nonnull, nonatomic, strong) DrawingView *drawingView;
@property (nonnull, nonatomic, strong) UIScrollView *scrollView;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    [self initControls];
    [self setTheme];
    [self doLayout];
}


- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (void)initControls {
    self.paletteView = [[PaletteView alloc] initWithFrame:CGRectMake(0.0, 0.0, self.view.frame.size.width, 100.0)];
    self.drawingView = [[DrawingView alloc] initWithFrame:self.view.frame];
    self.scrollView = [[UIScrollView alloc] init];
}

- (void)setTheme {
    [self.paletteView setDelegate:self];
    [self.paletteView setBackgroundColor:[UIColor whiteColor]];
    [self.drawingView setBackgroundColor:[UIColor whiteColor]];

    [self.scrollView setDelegate:self];
    [self.scrollView setScrollEnabled:NO];
    [self.scrollView setMinimumZoomScale:1.0];
    [self.scrollView setMaximumZoomScale:2.0];
}

- (void)doLayout {
    [self.view addSubview:self.paletteView];
    [self.view addSubview:self.scrollView];

    [self.paletteView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
    [self.paletteView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
    [self.paletteView.topAnchor constraintEqualToAnchor:self.view.topAnchor].active = YES;

    [self.scrollView.leftAnchor constraintEqualToAnchor:self.view.leftAnchor].active = YES;
    [self.scrollView.rightAnchor constraintEqualToAnchor:self.view.rightAnchor].active = YES;
    [self.scrollView.topAnchor constraintEqualToAnchor:self.paletteView.bottomAnchor].active = YES;
    [self.scrollView.bottomAnchor constraintEqualToAnchor:self.view.bottomAnchor].active = YES;

    [self.scrollView setTranslatesAutoresizingMaskIntoConstraints:NO];


    //Layout drawingView
    [self.scrollView addSubview:self.drawingView];
}

- (nullable UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
    return self.drawingView;
}

- (void)didSelectColour:(PaletteView * _Nonnull)paletteView colour:(UIColor * _Nonnull)colour {
    [self.drawingView setPaletteColour:colour];
}
@end

      

+1


source







All Articles