问题
I am having an issue when trying to sketch on a UIView
that is a child view in a UIScrollView
.
The problem occurs when you try to sketch on the UIView
after it's been zoomed or scrolled on. It seems my sketch code (shown below) does not take into account the zoom or scale of the UIView
within the UIScrollView
because the lines blur and don't show up where they should. Please let me know if there is something that can be done to 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 should add that this code works fine if the UIView
has not been zoomed or scrolled on. Also sheetDrawView is a subview of the UIScrollView
.
回答1:
Created a small drawing application (Only tested on iPhone 7 Plus 10.1 Simulator).
First I created the PalleteView. This view allows you to select colours to draw 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
It's a simple collectionView with each cell being coloured. I hardcoded the sizes of the cells.
Next I created the DrawingView. This is the view that the user will draw on using their finger. This view only handles ONE finger at a time drawing. I took the idea of drawing to a Bitmap first from GameDesign. In games, you want to draw textures to memory first. Then when you are finished doing all your drawing, you Blit that frame to the screen. This improves speed significantly because you aren't updating the screen after EVERY operation. Instead, you are updating the screen at the end of all drawing (when the user lifts their finger).
To accomplish 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 chose to use drawInRect:blendMode:alpha
because that would allow you to draw with different blending options and alpha levels. For this example, I am drawing fully Opaque 32-bit BGRA bitmaps.
Next I created the controller with an embedded ScrollView. This allows the user to ZOOM-IN/Scale the view AND draw on the zoomed-in/scaled view. When you zoom back out, you will notice the drawing is exact (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
来源:https://stackoverflow.com/questions/40835952/sketch-on-uiview-within-uiscrollview