how to make the stroke smooth? - ios

i have an paint app using bezierpath, i want the lines im creating is smooth. My paint app create lines that are like ridges line. and it is taking me a week already please help me.
this is my view controller it call the stroke subview.
viewController.h
#import <UIKit/UIKit.h>
#class Strokes;
#interface ViewController : UIViewController {
Strokes *strokes;
UISegmentedControl *colorControl;
UISegmentedControl *bkControl;
UISlider *slider;
}
#property (nonatomic, retain) IBOutlet UISegmentedControl *bkControl;
#property (nonatomic, retain) IBOutlet UISegmentedControl *colorControl;
#property (nonatomic, retain) IBOutlet Strokes *strokes;
#property (nonatomic, retain) IBOutlet UISlider *slider;
- (IBAction)sizSlider:(id)sender;
- (IBAction)pickColor:(id)sender;
- (IBAction)clearAll:(id)sender;
- (IBAction)undoStroke: (id)sender;
- (IBAction)showGrid:(id)sender;
#end
viewController.m
#import "ViewController.h"
#import "Strokes.h"
#implementation SecondViewController
#synthesize strokes;
#synthesize colorControl;
#synthesize bkControl;
#synthesize slider;
- (IBAction)clearAll:(id)sender {
[strokes.strokeArray removeAllObjects];
[strokes setNeedsDisplay];
}
- (IBAction)undoStroke: (id)sender {
if ([strokes.strokeArray count]>0) {
[strokes.strokeArray removeLastObject];
[strokes setNeedsDisplay];
}
}
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
// Return YES for supported orientations
return (interfaceOrientation == UIInterfaceOrientationPortrait);
}
- (IBAction)sizSlider:(id)sender{
strokes.strokeSize=(int)slider.value;
}
- (IBAction)showGrid:(id)sender {
if (bkControl.selectedSegmentIndex==0) {
UIColor *bkg = [[UIColor alloc] initWithPatternImage: [UIImage imageNamed: #"grid.jpg"]];
strokes.backgroundColor=bkg;
}
else if (bkControl.selectedSegmentIndex==1) {
UIColor *bkg = [UIColor colorWithRed:0 green:0 blue:0 alpha:0];;
strokes.backgroundColor=bkg;
}
}
- (IBAction)pickColor:(id)sender {
if (colorControl.selectedSegmentIndex==0) {
strokes.strokeColor=[UIColor colorWithRed:0 green:0 blue:0 alpha:1];
}
else if (colorControl.selectedSegmentIndex==1) {
strokes.strokeColor=[UIColor colorWithRed:250 green:0 blue:0 alpha:1];
}
else if (colorControl.selectedSegmentIndex==2) {
strokes.strokeColor=[UIColor colorWithRed:100 green:100 blue:0 alpha:1];
}
else if (colorControl.selectedSegmentIndex==3) {
strokes.strokeColor=[UIColor colorWithRed:0 green:0 blue:250 alpha:1];
}
else if (colorControl.selectedSegmentIndex==4) {
strokes.strokeColor=[UIColor colorWithRed:0 green:250 blue:0 alpha:1];
}
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[slider release];
[strokes release];
[colorControl release];
[bkControl release];
[super dealloc];
}
#end
my stroke which create the lines, on the view.
stroke.h
import
#interface Strokes : UIView {
NSMutableArray *strokeArray;
UIColor *strokeColor;
int strokeSize;
}
#property (nonatomic, retain) UIColor *strokeColor;
#property (nonatomic) int strokeSize;
#property (nonatomic, retain) NSMutableArray *strokeArray;
#end
stroke.m
#import "Strokes.h"
#implementation Strokes
#synthesize strokeColor;
#synthesize strokeSize;
#synthesize strokeArray;
- (void) awakeFromNib
{
self.strokeArray = [[NSMutableArray alloc] init];
self.strokeColor = [UIColor colorWithRed:0 green:0 blue:232 alpha:1];
self.strokeSize = 1;
}
- (void) drawRect: (CGRect)rect
{
NSMutableArray *stroke;
for (stroke in strokeArray) {
CGContextRef contextRef = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(contextRef, [[stroke objectAtIndex:1] intValue]);
CGFloat *color = CGColorGetComponents([[stroke objectAtIndex:2] CGColor]);
CGContextSetRGBStrokeColor(contextRef, color[0], color[1], color[2], color[3]);
CGContextBeginPath(contextRef);
CGPoint points[[stroke count]];
for (NSUInteger i = 3; i < [stroke count]; i++) {
points[i-3] = [[stroke objectAtIndex:i] CGPointValue];
}
CGContextAddLines(contextRef, points, [stroke count]-3);
CGContextStrokePath(contextRef);
}
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch;
NSEnumerator *counter = [touches objectEnumerator];
while ((touch = (UITouch *)[counter nextObject])) {
NSValue *touchPos = [NSValue valueWithCGPoint:[touch locationInView:self]];
UIColor *color = [UIColor colorWithCGColor:strokeColor.CGColor];
NSNumber *size = [NSNumber numberWithInt:strokeSize];
NSMutableArray *stroke = [NSMutableArray arrayWithObjects: touch, size, color, touchPos, nil];
[strokeArray addObject:stroke];
}
}
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch;
NSEnumerator *counter = [touches objectEnumerator];
while ((touch = (UITouch *)[counter nextObject])) {
NSMutableArray *stroke;
for (stroke in strokeArray) {
if ([stroke objectAtIndex:0] == touch) {
[stroke addObject:[NSValue valueWithCGPoint:[touch locationInView:self]]];
}
[self setNeedsDisplay];
}
}
}
- (void)dealloc {
[strokeColor release];
[super dealloc];
}
#end

look at this code by Erica Sadun the code use BezierPath and the Catmull-Rom splining algorithm to improve performance. You can Also have a look at her code to draw on screen

Related

My custom UIButton can't be used to replace leftBarButtonItem

I created a custom UIButton subclassing from UIControl (It is called "OZCustomButton"). It works fine in Storyboard, however when I was trying to use it to replace the leftBarButtonItem back button programmatically, it had a problem with its layout.
Here is the code I use to replace the leftBarButtonItem.
OZCustomButton *customBackButton = [[OZCustomButton alloc] initWithFrame:CGRectZero];
customBackButton.buttonImage = [UIImage imageNamed:#"BackArrow"];
customBackButton.buttonText = #"Back";
[customBackButton sizeToFit];
NSLog(#"this size: %#", NSStringFromCGRect(customBackButton.frame));
UIBarButtonItem *item = [[UIBarButtonItem alloc] initWithCustomView:customBackButton];
self.navigationItem.leftBarButtonItem = item;
But nothing is showing on the left side of the navigation bar.
Navigation bar:
And the Dubug View Hierarchy tool shows these warning messages:
It seems the customBackButton is in the View Hierarchy, but the layout is not correct.
This is the code for my OZCustomButton.
OZCustomButton.h:
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
IB_DESIGNABLE
#interface OZCustomButton : UIControl <NSCoding>
#property (assign, nonatomic) IBInspectable CGFloat borderWidth;
#property (assign, nonatomic) IBInspectable CGFloat borderRadius;
#property (strong, nonatomic) IBInspectable UIColor *borderColor;
#property (strong, nonatomic) IBInspectable UIColor *fillColor;
#property (strong, nonatomic) IBInspectable UIColor *tintColor;
#property (strong, nonatomic) IBInspectable NSString *buttonText;
#property (assign, nonatomic) IBInspectable CGFloat textSize;
#property (assign, nonatomic) IBInspectable BOOL isTextBold;
#property (strong, nonatomic) UIFont *textFont;
#property (nullable, strong, nonatomic) IBInspectable UIImage *buttonImage;
#property (nullable, strong, nonatomic) NSArray *gradientColors;
#end
NS_ASSUME_NONNULL_END
OZCustomButton.m
#import "OZCustomButton.h"
#import "UIColor+Custom.h"
#import "CAGradientLayer+Utilities.h"
#interface OZCustomButton ()
#property (strong, nonatomic) UILabel *buttonLabel;
#property (nullable, strong, nonatomic) UIImageView *buttonImageView;
#property (nullable, strong, nonatomic) CAGradientLayer *gradientLayer;
#property (nullable, strong, nonatomic) UIStackView *stackView;
#end
#implementation OZCustomButton
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
[self setupDefaults];
}
return self;
}
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self setupDefaults];
}
return self;
}
- (void)layoutLabelAndImageView {
_buttonLabel = [[UILabel alloc] initWithFrame:CGRectZero];
_buttonLabel.numberOfLines = 1; // need to set to 1
_buttonLabel.textAlignment = NSTextAlignmentCenter;
//_buttonLabel.backgroundColor = [UIColor redColor];
_buttonImageView = [[UIImageView alloc] initWithFrame:CGRectZero];
_buttonImageView.contentMode = UIViewContentModeScaleAspectFit;
//_buttonImageView.backgroundColor = [UIColor redColor];
_stackView = [[UIStackView alloc] init];
_stackView.axis = UILayoutConstraintAxisHorizontal;
_stackView.alignment = UIStackViewAlignmentCenter;
_stackView.distribution = UIStackViewDistributionFillProportionally;
_stackView.spacing = 8;
_stackView.userInteractionEnabled = NO;
[_stackView addArrangedSubview:_buttonImageView];
[_stackView addArrangedSubview:_buttonLabel];
_stackView.translatesAutoresizingMaskIntoConstraints = false;
[self addSubview:_stackView];
[[_stackView.centerXAnchor constraintEqualToAnchor:self.centerXAnchor] setActive:YES];
[[_stackView.centerYAnchor constraintEqualToAnchor:self.centerYAnchor] setActive:YES];
}
- (void)layoutGradientLayer {
_gradientLayer = [CAGradientLayer createGradientLayerWithBounds:self.bounds
colors:nil
direction:GradientFromLeftTopToRightBottom
locations:#[#0.0, #1.0]];
_gradientLayer.anchorPoint = CGPointMake(0, 0);
[self.layer insertSublayer:_gradientLayer below:_stackView.layer];
}
- (void)setupDefaults {
_borderWidth = 0.0f;
_borderRadius = 0.0f;
_borderColor = [UIColor blackColor];
_fillColor = [UIColor whiteColor];
_buttonText = #"Button";
_tintColor = [UIColor blackColor];
_textSize = 17.0f;
_isTextBold = false;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
_gradientColors = nil;
_buttonImage = nil;
[self layoutLabelAndImageView];
[self updateView];
}
- (void)updateView {
self.layer.borderColor = _borderColor.CGColor;
self.layer.borderWidth = _borderWidth;
self.layer.cornerRadius = _borderRadius;
self.layer.masksToBounds = true;
self.layer.backgroundColor = _fillColor.CGColor;
// update button text label
_buttonLabel.text = _buttonText;
_buttonLabel.textColor = _tintColor;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
_buttonLabel.font = _textFont;
_buttonLabel.textAlignment = NSTextAlignmentCenter;
[_buttonLabel sizeToFit];
// update button image
if (_buttonImage != nil) {
_buttonImageView.hidden = NO;
_buttonImageView.image = [_buttonImage imageWithRenderingMode:UIImageRenderingModeAlwaysTemplate];
_buttonImageView.tintColor = _tintColor;
[_buttonImageView sizeToFit];
} else {
_buttonImageView.image = nil;
_buttonImageView.hidden = YES;
[_buttonImageView sizeToFit];
}
// update gradient layer
if (_gradientColors != nil) {
// if gradient layer is not initialized, call layoutGradientLayer()
if (_gradientLayer == nil) {
[self layoutGradientLayer];
}
// transform the UIColor to CGColorRef
NSMutableArray *colors = [NSMutableArray arrayWithCapacity:_gradientColors.count];
for (UIColor *color in _gradientColors) {
[colors addObject:(id)[color CGColor]];
}
if (colors.count > 0) {
_gradientLayer.colors = [colors copy];
}
}
}
#pragma mark - setters
- (void)setTextSize:(CGFloat)textSize {
if (_textSize != textSize) {
_textSize = textSize;
_textFont = _isTextBold ? [UIFont fontWithName:#"AlteHaasGrotesk_Bold" size:_textSize] : [UIFont fontWithName:#"AlteHaasGrotesk" size:_textSize];
[self updateView];
}
}
- (void)setBorderColor:(UIColor *)borderColor {
if (_borderColor != borderColor) {
_borderColor = borderColor;
[self updateView];
}
}
- (void)setBorderWidth:(CGFloat)borderWidth {
if (_borderWidth != borderWidth) {
_borderWidth = borderWidth;
[self updateView];
}
}
- (void)setBorderRadius:(CGFloat)borderRadius {
if (_borderRadius != borderRadius) {
_borderRadius = borderRadius;
[self updateView];
}
}
- (void)setFillColor:(UIColor *)fillColor {
if (_fillColor != fillColor) {
_fillColor = fillColor;
[self updateView];
}
}
- (void)setTintColor:(UIColor *)tintColor {
if (_tintColor != tintColor) {
_tintColor = tintColor;
[self updateView];
}
}
- (void)setButtonText:(NSString *)buttonText {
if (_buttonText != buttonText) {
_buttonText = buttonText;
[self updateView];
}
}
- (void)setTextFont:(UIFont *)textFont {
if (_textFont != textFont) {
_textFont = textFont;
[self updateView];
}
}
- (void)setGradientColors:(NSArray *)gradientColors {
if (_gradientColors != gradientColors) {
_gradientColors = gradientColors;
[self updateView];
}
}
- (void)setButtonImage:(UIImage *)buttonImage {
if (_buttonImage != buttonImage) {
_buttonImage = buttonImage;
[self updateView];
}
}
- (void)setIsTextBold:(BOOL)isTextBold {
if (_isTextBold != isTextBold) {
_isTextBold = isTextBold;
[self updateView];
}
}
#pragma mark - UIControl actions
- (void)setHighlighted:(BOOL)highlighted {
[super setHighlighted:highlighted];
CABasicAnimation *fadeAnimation = [CABasicAnimation animationWithKeyPath:#"opacity"];
fadeAnimation.timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
fadeAnimation.duration = 0.2f;
if (highlighted) {
fadeAnimation.toValue = #0.6f;
} else {
fadeAnimation.toValue = #1.0f;
}
self.buttonLabel.layer.opacity = [fadeAnimation.toValue floatValue];
self.layer.opacity = [fadeAnimation.toValue floatValue];
[self.buttonLabel.layer addAnimation:fadeAnimation forKey:#"textFadeAnimation"];
[self.layer addAnimation:fadeAnimation forKey:#"backgroundFadeAnimation"];
}
- (void)setEnabled:(BOOL)enabled {
[super setEnabled:enabled];
if (enabled) {
self.layer.backgroundColor = self.fillColor.CGColor;
self.buttonLabel.textColor = self.tintColor;
} else {
self.layer.backgroundColor = [UIColor lighterColorForColor:self.fillColor].CGColor;
self.buttonLabel.textColor = [UIColor lightGrayColor];
}
}
#pragma mark - Override functions
- (void)layoutSubviews {
[super layoutSubviews];
[self updateView];
}
- (CGSize)sizeThatFits:(CGSize)size {
CGFloat minWidth = _buttonImageView.frame.size.width + 8 + _buttonLabel.frame.size.width;
CGFloat minHeight = MAX(_buttonImageView.frame.size.height, _buttonLabel.frame.size.height);
return CGSizeMake(minWidth + 6, minHeight + 4);
}
#end
I found one solution. Instead of setting the centenX anchor and centerY anchor, I change it to using left, right, top and bottom anchors:
[[_stackView.topAnchor constraintEqualToAnchor:_stackView.superview.topAnchor] setActive:YES];
[[_stackView.bottomAnchor constraintEqualToAnchor:_stackView.superview.bottomAnchor] setActive:YES];
[[_stackView.leftAnchor constraintEqualToAnchor:_stackView.superview.leftAnchor] setActive:YES];
[[_stackView.rightAnchor constraintEqualToAnchor:_stackView.superview.rightAnchor] setActive:YES];
Then it got the layout correctly.
If you have any other methods to solve the problem, please let me know.

How can I animate a UIColor in a CALayer?

I have a custom CALayer within which I'm trying to enable the animation of certain properties using actionForKey and following this tutorial.
I have a CGFloat property that will change perfectly when inside an animation block but my other property, a UIColor, will not.
Here's my function:
- (id<CAAction>)actionForKey:(NSString *)event {
if ([self presentationLayer] != nil && [[self class] isCustomAnimationKey:event]) {
id animation = [super actionForKey:#"backgroundColor"];
if (animation == nil || [animation isEqual:[NSNull null]]) {
[self setNeedsDisplay];
return [NSNull null];
}
[animation setKeyPath:event];
[animation setFromValue:[self.presentationLayer valueForKey:event]];
[animation setToValue:nil];
return animation;
}
return [super actionForKey:event];
}
The colour is being set using CGContextSetFillColorWithColor but I can see from logs that the colour simply changes from one to the next without any of the interpolated values.
Any ideas?
It turned out to be obvious in the end, I needed to expose a CGColor property in my CALayer and animate that instead.
Edit:
Here's some code for this, using the UIViewCustomPropertyAnimation project as a basis.
In OCLayer.h add a new property:
#property (nonatomic) CGColorRef myColor;
In OCLayer.m add the #dynamic directive:
#dynamic myColor;
And update isCustomAnimKey:
+ (BOOL)isCustomAnimKey:(NSString *)key {
return [key isEqualToString:#"percent"] || [key isEqualToString:#"myColor"];
}
In OCView.h add the same property but as a UIColor. This already existed in my project so didn't require modification, which is great because it didn't break any code.
#property (nonatomic, strong) UIColor *progressColor;
The main changes would be in OCView.m as the getter and setter need to convert from CGColor to UIColor and back again.
- (void)setMyColor:(UIColor *)color {
self.layer.myColor = color.CGColor;
}
- (UIColor*)myColor {
return [UIColor colorWithCGColor: self.layer.myColor];
}
The animation can now be carried out as normal:
[UIView animateWithDuration:1.f animations:^{
self.animView.myColor = [UIColor redColor];
}];
This is my code and not an answer. There are three classes: OCLayer, OCView and OCViewController. You can see the value of "percent" is changing during animation while the value of "myColor" won't.
#interface OCLayer : CALayer
#property (nonatomic) CGFloat percent;
#property (nonatomic) CGColorRef myColor;
#end
#import "OCLayer.h"
#implementation OCLayer
#dynamic percent;
#dynamic myColor;
- (id<CAAction>)actionForKey:(NSString *)key
{
if ([[self class] isCustomAnimKey:key])
{
id animation = [super actionForKey:#"backgroundColor"];
if (animation == nil || [animation isEqual:[NSNull null]])
{
[self setNeedsDisplay];
return [NSNull null];
}
[animation setKeyPath:key];
[animation setFromValue: [self.presentationLayer valueForKey:key]];
[animation setToValue : nil];
return animation;
}
return [super actionForKey:key];
}
- (id)initWithLayer:(id)layer
{
self = [super initWithLayer:layer];
if (self)
{
if ([layer isKindOfClass:[OCLayer class]])
{
self.percent = ((OCLayer *)layer).percent;
}
}
return self;
}
+ (BOOL)needsDisplayForKey:(NSString *)key
{
if ([self isCustomAnimKey:key]) return true;
return [super needsDisplayForKey:key];
}
+ (BOOL)isCustomAnimKey:(NSString *)key
{
return [key isEqualToString:#"percent"] || [key isEqualToString:#"myColor"];
}
#end
#interface OCView : UIView
#property (weak, nonatomic) IBOutlet UIView *percentView;
#property (weak, nonatomic) IBOutlet UILabel *label;
#property (nonatomic, strong) UIColor * myColor;
//- (UIColor*)myColor ;
//- (void)setMyColor:(UIColor *)color;
- (CGFloat )percent;
- (void)setPercent:(CGFloat )percent;
#end
#import "OCView.h"
#import "OCLayer.h"
#implementation OCView
- (void)displayLayer:(CALayer *)layer
{
CGFloat percent = [(OCLayer *)[self.layer presentationLayer] percent];
CGColorRef myColor = [(OCLayer *)[self.layer presentationLayer] myColor];
NSLog(#"%f", percent);
NSLog(#"%#", myColor);
self.percentView.backgroundColor = [[UIColor alloc]initWithCGColor: myColor];
self.label.text = [NSString stringWithFormat:#"%.0f", floorf(percent)];
}
+ (Class)layerClass
{
return [OCLayer class];
}
- (void)setPercent:( CGFloat )percent
{
((OCLayer *)self.layer).percent = percent;
}
- (CGFloat )percent
{
return ((OCLayer *)self.layer).percent;
}
- (void)setMyColor:(UIColor *)color {
((OCLayer *)self.layer).myColor = color.CGColor;
}
- (UIColor*)myColor {
return [UIColor colorWithCGColor: ((OCLayer *)self.layer).myColor];
}
#end
#interface OCViewController : UIViewController
#property (weak, nonatomic) IBOutlet OCView *animView;
#end
#import "OCViewController.h"
#import "OCLayer.h"
#interface OCViewController ()
#end
#implementation OCViewController
-(void)viewDidAppear:(BOOL)animated{
[super viewDidAppear:animated];
self.animView.percent = 1;
self.animView.myColor = [UIColor whiteColor];
[UIView animateWithDuration:3.0
animations:^{
self.animView.percent = 20;
self.animView.myColor = [UIColor redColor];
}];
}
- (void)viewDidLoad {
[super viewDidLoad];
}
#end

LoadView is never called on UIViewController

I'm trying to replicate the implementation In iOS, how do I create a button that is always on top of all other view controllers? in Objective-C.
But the loadView is never called, can someone tell me why?
And how to fix it?
ApplicationFloatingButtonWindow.m
#import "ApplicationFloatingButtonWindow.h"
#import "ApplicationFloatingButtonController.h"
#interface ApplicationFloatingButtonWindow()
#pragma mark - Properties
#property (strong, nonatomic) UIButton *floatingButton;
#property (strong, nonatomic) ApplicationFloatingButtonController *floatingButtonViewController;
#end
#implementation ApplicationFloatingButtonWindow
#pragma mark - Initialization
- (instancetype)init {
self = [super init];
if (self) {
self.backgroundColor = nil; // make background transparent
}
return self;
}
#pragma mark - Gesture Handling
- (BOOL)pointInside:(CGPoint)point withEvent:(UIEvent *)event {
if(self.floatingButton) {
CGPoint floatingButtonPoint = [self convertPoint:point toView:self.floatingButton];
return [self.floatingButton pointInside:floatingButtonPoint withEvent:event];
}
return [super pointInside:point withEvent:event];
}
#end
ApplicationFloatingButtonController.h
#import <UIKit/UIKit.h>
#import "ApplicationFloatingButtonWindow.h"
#interface ApplicationFloatingButtonController : UIViewController
#pragma mark - Properties
#property (strong, nonatomic) UIButton *floatingButton;
#property (strong, nonatomic) ApplicationFloatingButtonWindow *window;
#end
ApplicationFloatingButtonController.m
#import "ApplicationFloatingButtonController.h"
#interface ApplicationFloatingButtonController ()
#end
#implementation ApplicationFloatingButtonController
#pragma mark - Initialization
- (instancetype)init {
self = [super initWithNibName:nil bundle:nil];
if (self) {
[self setup];
}
return self;
}
#pragma mark - Setup
- (void)setup {
self.window.windowLevel = CGFLOAT_MAX;
self.window.hidden = NO;
self.window.rootViewController = self;
[NSNotificationCenter.defaultCenter addObserver:self selector:#selector(keyboardDidShow) name:UIKeyboardDidShowNotification object:nil];
}
- (void)setupPanGestureRecognizers {
UIPanGestureRecognizer *panGestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(panGestureRecognizerAction:)];
[self.floatingButton addGestureRecognizer:panGestureRecognizer];
}
#pragma mark - Lifecycle
- (void)loadView {
UIButton *button = [self createButton];
UIView *view = [UIView new];
[view addSubview:button];
self.view = view;
self.floatingButton = button;
[self setupPanGestureRecognizers];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self snapButtonToBestPosition];
}
#pragma mark - Elements Factory
- (UIButton *)createButton {
UIButton *button = [UIButton buttonWithType:UIButtonTypeInfoDark];
[button setTitle:#"Floating..." forState:UIControlStateNormal];
[button setTitleColor:UIColor.blackColor forState:UIControlStateNormal];
button.backgroundColor = UIColor.whiteColor;
button.layer.shadowColor = UIColor.blackColor.CGColor;
button.layer.shadowRadius = 3;
button.layer.shadowOpacity = 0.8;
button.layer.shadowOffset = CGSizeZero;
[button sizeToFit];
button.frame = CGRectMake(10, 10, 24, 24);
button.autoresizingMask = (UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight);;
return button;
}
#pragma mark - Selectors
- (void)keyboardDidShow {
// This refreshes the window level and puts it back to the top most view
self.window.windowLevel = 0;
self.window.windowLevel = CGFLOAT_MAX;
}
- (void)panGestureRecognizerAction:(UIPanGestureRecognizer *)panGestureRecognizer {
CGPoint gestureOffset = [panGestureRecognizer translationInView:self.view];
[panGestureRecognizer setTranslation:CGPointZero inView:self.view];
CGPoint newButtonCenter = self.floatingButton.center;
newButtonCenter.x += gestureOffset.x;
newButtonCenter.y += gestureOffset.y;
self.floatingButton.center = newButtonCenter;
if (panGestureRecognizer.state == UIGestureRecognizerStateEnded || panGestureRecognizer.state == UIGestureRecognizerStateCancelled ) {
[UIView animateWithDuration:0.3 animations:^{
[self snapButtonToBestPosition];
}];
}
}
#pragma mark - Elements Layout and Positioning Methods
- (NSArray *)possibleButtonPositions {
CGSize buttonSize = self.floatingButton.bounds.size;
CGRect rect = CGRectInset(self.view.frame, 4 + buttonSize.width / 2, 4 + buttonSize.height / 2);
NSMutableArray *possiblePositions = [NSMutableArray new];
[possiblePositions addObject:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect))]];
[possiblePositions addObject:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect))]];
[possiblePositions addObject:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect))]];
[possiblePositions addObject:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect))]];
[possiblePositions addObject:[NSValue valueWithCGPoint:CGPointMake(CGRectGetMidX(rect), CGRectGetMidY(rect))]];
return possiblePositions;
}
- (void)snapButtonToBestPosition {
CGPoint bestPositionForButton = CGPointZero;
CGFloat distanceToBestPosition = CGFLOAT_MAX;
CGPoint buttonCenter = self.floatingButton.center;
for (NSValue *possibleButtonPositionAsNSValue in [self possibleButtonPositions]) {
CGPoint possibleButtonPosition = possibleButtonPositionAsNSValue.CGPointValue;
CGFloat distance = hypot(buttonCenter.x - possibleButtonPosition.x, buttonCenter.y - possibleButtonPosition.y);
if (distance < distanceToBestPosition) {
distanceToBestPosition = distance;
bestPositionForButton = possibleButtonPosition;
}
}
self.floatingButton.center = bestPositionForButton;
}
#end
AppDelegate.m
#import "AppDelegate.h"
#import "ApplicationFloatingButtonController.h"
#interface AppDelegate ()
#pragma mark - Properties
#property (strong, nonatomic) ApplicationFloatingButtonController *floatingButtonController;
#end
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// Override point for customization after application launch.
// Configure Floating Button
self.floatingButtonController = [ApplicationFloatingButtonController new];
[self.floatingButtonController.floatingButton addTarget:self action:#selector(floatingButtonDidReceiveTouchUpInside) forControlEvents:UIControlEventTouchUpInside];
return YES;
}
- (void)floatingButtonDidReceiveTouchUpInside {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:#"My Alert"
message:#"This is an alert."
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:#"OK" style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[self.window.rootViewController presentViewController:alert animated:YES completion:nil];
}
#end
The solution and final code is here:
https://github.com/bocato/ApplicationFloatingButton
The Swift version has
private let window = FloatingButtonWindow()
in the FloatingButtonController class. You have created the property, in the .h file, but you don't initialise it anywhere. Try adding
self.window = [ApplicationFloatingButtonWindow new];
to your setup method.

Sketch On UIView Within UIScrollView

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.
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

Communication between A ViewController and its View

I created two classes 1UIViewControllerClass and its 1UIViewClass (Which is the View of the ViewController). On my UIViewClass I have two methods, one of them is touchesBegan, which is getting the location of the touched point. The second Methode, which only runs on the View, takes the location information and gets the color at this location in view. The second Methode returns with an UIColor.
After these steps the UIColor should be send to the UIViewController.
I tried to get the UIColor Variable to the ViewController (Delegate and Instanzing), but nothing worked.
The code is below.
Update: Tried one answer but it did not work. Updated this code.
Here is FarbView.h:
#import <UIKit/UIKit.h>
#protocol FarbDelegate <NSObject>
#required
- (void)receiveNewColor:(UIColor*)color;
#end
#interface FarbView :UIView {
__weak id <FarbDelegate> delegate;
}
#property (nonatomic, weak) id <FarbDelegate> delegate;
#property (strong,nonatomic) UIColor *pickedColor;
- (UIColor *) colorOfPoint:(CGPoint)point;
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event;
#end
Here is FarbView.m:
#import "FarbView.h"
#import <QuartzCore/QuartzCore.h>
#implementation FarbView
#synthesize delegate;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
//Get Location of touched Point
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.pickedColor = [[UIColor alloc]init];
UITouch *touch = [[event allTouches] anyObject];
CGPoint loc = [touch locationInView:self];
NSLog(#"%#", NSStringFromCGPoint(loc));
self.pickedColor = [self colorOfPoint:loc];
//if you will describe receiveNewColor method on your view controller class we send new color message.
if([delegate respondsToSelector:#selector(receiveNewColor:)]){
[delegate receiveNewColor:self.pickedColor];
}
}
//Getting Color at Location
- (UIColor *) colorOfPoint:(CGPoint)point
{
unsigned char pixel[4] = {0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(pixel, 1, 1, 8, 4, colorSpace, kCGImageAlphaPremultipliedLast);
CGContextTranslateCTM(context, -point.x, -point.y);
[self.layer renderInContext:context];
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
NSLog(#"pixel: %d %d %d %d", pixel[0], pixel[1], pixel[2], pixel[3]);
UIColor *color = [UIColor colorWithRed:pixel[0]/255.0 green:pixel[1]/255.0 blue:pixel[2]/255.0 alpha:pixel[3]/255.0];
return color;
}
Next is FarbViewController.h
#import <UIKit/UIKit.h>
#import "FarbView.h"
#interface FarbViewController:UIViewController <FarbDelegate>
#property (strong, nonatomic) IBOutlet UILabel *currentColor;
#property (strong, nonatomic) FarbView *farbview;
-(void)receiveNewColor:(UIColor *)color;
#end
And FarbViewController.m
#import "FarbViewController.h"
#interface FarbViewController ()
#end
#implementation FarbViewController
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"Richtige Page 1");
self.farbview =[[FarbView alloc]init];
self.farbview.delegate = self;
// Do any additional setup after loading the view.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)receiveNewColor:(UIColor *)color{
NSLog(#"New color selected %#", color);
//your code here
}
#end
I don't recommend to use NSNotificationCenter here.
The best way to receive callback from child is Delegate pattern.
FarbView.h file:
#protocol FarbDelegate <NSObject>
#required
- (void)receiveNewColor:(UIColor*)color;
#end
#interface FarbView :UIView{
__weak id <FarbDelegate> delegate;
//...
}
#property (nonatomic, weak) id <FarbDelegate> delegate;
FarbView.m touch began handler:
//Get Location of touched Point
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
self.pickedColor = [[UIColor alloc]init];
UITouch *touch = [[event allTouches] anyObject];
CGPoint loc = [touch locationInView:self];
NSLog(#"%#", NSStringFromCGPoint(loc));
self.pickedColor = [self colorOfPoint:loc];
//if you will describe receiveNewColor method on your view controller class we send new color message.
if([delegate respondsToSelector:#selector(receiveNewColor:)]){
[delegate receiveNewColor:self.pickedColor];
}
NSLog(#"Color: %#",self.pickedColor);
}
In ViewController class add declaration of method receiveNewColor:
-(void)receiveNewColor:(UIColor)color{
NSLog(#"New color selected %#", color);
//your code here
}
And don't forget add in viewDidLoad method next line of code:
//self.farbView - its your object of FarbView class
self.farbView.delegate = self;
Here you will have warning. Just add "FarbDelegate" into #interface line :
#interface FarbViewController:UIViewController<FarbDelegate>

Resources