How can I use an IBAction from inside my controller to draw a circle in my custom view? - objective-c

I draged out a generic view and hooked it up to my circleView.m. I then dragged out a round rect button on top of that view and hooked up an IBAction to it. As of right now, when the view loads the circle is automatically drawn onto the screen. What I would like to do is draw the circle on the screen only when the button is pressed using drawRect or some other draw method. Here is my code:
drawCircleViewController.h
#import <UIKit/UIKit.h>
#interface drawCircleViewController : UIViewController
#end
drawCircleViewController.m
#import "drawCircleViewController.h"
#import "circleView.h"
#interface drawCircleViewController()
#property (nonatomic, weak) IBOutlet circleView *circleV;
#end
#implementation drawCircleViewController
#synthesize circleV = _circleV;
- (IBAction)buttonPressedToDrawCircle:(id)sender {
// This is action I want to use to draw the circle in my circleView.m
}
#end
circleView.h
#import <UIKit/UIKit.h>
#interface circleView : UIView
#end
circleView.m
#import "circleView.h"
#implementation circleView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawCircleAtPoint:(CGPoint)p
withRadius:(CGFloat)radius
inContext:(CGContextRef)context
{
UIGraphicsPushContext(context);
CGContextBeginPath(context);
CGContextAddArc(context, p.x, p.y, radius, 0, 2*M_PI, YES);
CGContextStrokePath(context);
UIGraphicsPopContext();
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGPoint midpoint;
midpoint.x = self.bounds.origin.x + self.bounds.size.width / 2;
midpoint.y = self.bounds.origin.y + self.bounds.size.height / 2;
#define DEFAULT_SCALE 0.90
CGFloat size = self.bounds.size.width / 2;
if (self.bounds.size.height < self.bounds.size.width) size = self.bounds.size.height / 2;
size *= DEFAULT_SCALE;
CGContextSetLineWidth(context, 5.0);
[[UIColor blueColor] setStroke];
[self drawCircleAtPoint:midpoint withRadius:size inContext:context];
}
#end

With what you have there, the simplest approach would probably be to make your circle view hidden and show it when the button is pressed.
Otherwise, you can keep a BOOL in your view to denote whether the button has been tapped and check that during drawRect: (and use setNeedsDisplay to trigger changes).

Related

Not getting UIImageView Rounded Border while Added Multiplier for UIImageViwe

In Storyboard I applied Multiplier for height and width to UIImageView then I just want to rounded border so I used below code its not work for all iPhones.
_profileImgView.clipsToBounds = YES;
_profileImgView.layer.backgroundColor = color.CGColor;
_profileImgView.layer.cornerRadius =_profileImgView.frame.size.width/2;
_profileImgView.layer.borderColor = [UIColor colorWithRed:253.0/255.0 green:182.0/255.0 blue:43.0/255.0 alpha:100].CGColor;
_profileImgView.layer.borderWidth = 5.0f;
Since your corner radius depends on your frame size, you need to update it whenever the frame size changes. If you are using storyboards for your design, you will get the frame size that is in the design when viewDidLoad is called. If the frame size differs for different devices, you will get the final size at a later point in time in the views layoutSubviews or possibly the view controller's viewDidLayoutSubviews.
My suggested solution is to sub-class UIImageView and put the specifics for the image view in awakeFromNib and layoutSubviews, then use this class instead of UIImageView where appropriate.
// CircularImageView.h
#import <UIKit/UIKit.h>
#interface CircularImageView : UIImageView
#end
// CircularImageView.m
#implementation CircularImageView
- (void)awakeFromNib {
[super awakeFromNib];
self.clipsToBounds = YES;
self.layer.borderColor = [UIColor colorWithRed:253.0/255.0 green:182.0/255.0 blue:43.0/255.0 alpha:100].CGColor;
self.layer.borderWidth = 5.0f;
}
- (void)layoutSubviews {
[super layoutSubviews];
self.layer.cornerRadius = self.frame.size.width / 2;
}
#end
implement this code in same Viewcontroller class it is work for me
-(void)viewDidLayoutSubviews
{
[super viewDidLayoutSubviews];
_profileImgView.clipsToBounds = YES;
_profileImgView.layer.backgroundColor = color.CGColor;
_profileImgView.layer.cornerRadius =_profileImgView.frame.size.width/2;
_profileImgView.layer.borderColor = [UIColor colorWithRed:253.0/255.0 green:182.0/255.0 blue:43.0/255.0 alpha:100].CGColor;
_profileImgView.layer.borderWidth = 5.0f;
}

COCOA: How to create a round button. It should be clickable only in its boundary

I am trying to create a round button which is only clickable in its boundaries.
What I have done
// imported QuartzCore
#import <QuartzCore/QuartzCore.h>
//created an IBOutlet for the button
IBOutlet NSButton* btn;
//defined the button height width (same)
#define ROUND_BUTTON_WIDTH_HEIGHT 150
//set corner radius
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
btn.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
btn.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}
//set view layer to YES
Problem
The button is clickable outside its boundaries.
When I am setting its position and when I am resizing the window it is getting back to its right position (the actual positon of the button is center of the window)
I have also tried to subclass NSButton and assign the class to the button but results are the same.
#import "roundBtn.h"
#import <QuartzCore/QuartzCore.h>
#implementation roundBtn
#define ROUND_BUTTON_WIDTH_HEIGHT 142
- (void)drawRect:(NSRect)dirtyRect {
[super drawRect:dirtyRect];
// Drawing code here.
self.frame = CGRectMake(0, 0, ROUND_BUTTON_WIDTH_HEIGHT, ROUND_BUTTON_WIDTH_HEIGHT);
self.layer.cornerRadius = ROUND_BUTTON_WIDTH_HEIGHT/2.0f;
}
#end
You can either reimplement the button behavior using a custom NSView and then it will respect the mouseDown events of a masksToBounds layer. NSButton doesn't work this way so the only way to tell your subclassed button that it won't be hit outside of the circle is to override the -hitTest: method to detect hits within the circle:
- (NSView *)hitTest:(NSPoint)aPoint {
NSPoint p = [self convertPoint:aPoint fromView:self.superview];
CGFloat midX = NSMidX(self.bounds);
CGFloat midY = NSMidY(self.bounds);
if (sqrt(pow(p.x-midX, 2) + pow(p.y-midY, 2)) < self.bounds.size.width/2.0) {
return self;
}
return nil;
}
In overriding this, you are telling the button that it will only register a hit if it is within the circle (using a little trigonometry). Returning self indicates the button was hit, returning nil indicates it was not hit.

UIView "stuck" in UITabBarController view top left corner

I'm initializing a loader subview to match the height, width and position of the UITabBar I'm using to wrap my app:
// In UITabBarController implementation
LoaderView *loaderView = [[LoaderView alloc] initWithFrame:[self tabBar].viewForBaselineLayout.frame];
[[self view] addSubview:loaderView];
//
// LoaderView.h
#import <UIKit/UIKit.h>
#interface LoaderView : UIView
#property (nonatomic, strong) UILabel *messageLabel;
#property (nonatomic, strong) NSString *message;
#property (nonatomic) CGRect frame;
- (void)createLabel;
- (void)drawLoader;
- (void)setText:(NSString *)newMessage;
- (void)show:(NSNotification *)notification;
#end
//
// LoaderView.m
#import "LoaderView.h"
#implementation LoaderView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self drawLoader];
}
return self;
}
- (void)drawLoader
{
UIColor *semiOpaqueGray = [[UIColor alloc] initWithRed:0.0f green:0.0f blue:0.0f alpha:0.8f];
[self setBackgroundColor:semiOpaqueGray];
[self createLabel];
}
- (void)createLabel
{
_messageLabel = [[UILabel alloc] initWithFrame: CGRectMake(15,9,([self frame].size.width - 10), 30)];
_messageLabel.textColor = [[UIColor alloc] initWithWhite:1.0 alpha:1];
_messageLabel.backgroundColor = [[UIColor alloc] initWithWhite:1.0 alpha:0.0];
[self addSubview:_messageLabel];
}
#end
The frame struct represents this incoming frame data:
2013-09-16 07:48:35.552 ---[97825:a0b] {{0, 519}, {320, 49}}
// Ostensibly 0,519 origin point and 320,49 w/h
The result is this. The mostly opaque dark box can be spotted in the top left corner. It looks like it's being positioned by its center point of the loader to the top left most point of the screen:
I can make the size of the box change, but I can't seem to move it from that top left position. Further, I set an animation on it, and that animation adjusts the frame (sliding it up an down from the tab bar area). That seems to have no effect either.
Thanks in advance for your help.
You can gently add your loadingView your application window. After set the frame of loaderView.
[loadView setFrame:CGRectMake(0,0,self.frame.size.width.,self.frame.size.height)];
[self.window addSubview:loaderView];
Then after loading finished.
[self.window removeSubview:loaderView];
Your superview is the problem. If you're adding it to your UINavigationController then the y-axis likely won't respond nicely. Try using the primary view of your controller
loaderView.frame = CGRectMake(x, 514, 40, 320); // made up numbers
[self.mapView addSubview:loaderView];
Also you're accessing your tabBar. Try using UIDevice window for getting your width and inherit the height from your labels on the bottom (or nest the labels within your LoaderView)
Also look into actually using UITabBar for handling this.

How to colorize the portion left of the knob on a NSSlider

In my project an NSSlider controls the volume of a AVPlayer. I'd like to colorize the portion of the NSSlider left of the knob. How can this be achieved?
You should use NSProgressIndicator for that.
As alternative, you can use a custom NSSliderCell and override - (BOOL)_usesCustomTrackImage to return YES and override - (void)drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped to draw your custom bar. There, you can use [NSCell doubleValue] to get the current position of the slider.
You should subclass NSSliderCell and write something like this:
#interface CustomSliderCell : NSSliderCell {
NSRect _barRect;
NSRect _currentKnobRect;
}
// You should set image for the barFill
// (or not if you want to use the default background)
// And image for the bar before the knob image
#property (strong, nonatomic) NSImage *barFillImage;
#property (strong, nonatomic) NSImage *barFillBeforeKnobImage;
// Slider also has the ages so you should set
// the different images for the left and the right one:
#property (strong, nonatomic) NSImage *barLeftAgeImage;
#property (strong, nonatomic) NSImage *barRightAgeImage;
#end
And implementation:
- (void)drawKnob:(NSRect)knobRect {
[super drawKnob:knobRect];
_currentKnobRect = knobRect;
}
-(void)drawBarInside:(NSRect)cellFrame flipped:(BOOL)flipped {
_barRect = cellFrame;
NSRect beforeKnobRect = [self createBeforeKnobRect];
NSRect afterKnobRect = [self createAfterKnobRect];
// Draw bar before the knob
NSDrawThreePartImage(beforeKnobRect, _barLeftAgeImage, _barFillBeforeKnobImage, _barFillBeforeKnobImage,
NO, NSCompositeSourceOver, 1.0, flipped);
// If you want to draw the default background
// add the following line at the at the beginning of the method:
// [super drawBarInside:cellFrame flipped:flipped];
// And comment the next line:
NSDrawThreePartImage(afterKnobRect, _barFillImage, _barFillImage, _barRightAgeImage,
NO, NSCompositeSourceOver, 1.0, flipped);
}
- (NSRect)createBeforeKnobRect {
NSRect beforeKnobRect = _barRect;
beforeKnobRect.size.width = _currentKnobRect.origin.x + _knobImage.size.width / 2;
beforeKnobRect.size.height = _barFillBeforeKnobImage.size.height;
beforeKnobRect.origin.y = beforeKnobRect.size.height / 2;
return beforeKnobRect;
}
- (NSRect)createAfterKnobRect {
NSRect afterKnobRect = _currentKnobRect;
afterKnobRect.origin.x += _knobImage.size.width / 2;
afterKnobRect.size.width = _barRect.size.width - afterKnobRect.origin.x;
afterKnobRect.size.height = _barFillImage.size.height;
afterKnobRect.origin.y = afterKnobRect.size.height / 2;
return afterKnobRect;
}
I've created LADSLider which will help you to
create what you want really simple and fast.

Custom UIProgressView drawing weirdness

I am trying to create my own custom UIProgressView by subclassing it and then overwrite the drawRect function.
Everything works as expected except the progress filling bar. I can't get the height and image right.
The images are both in Retina resolution and the Simulator is in Retina mode.
The images are called: "progressBar#2x.png" (28px high) and "progressBarTrack#2x.png" (32px high).
CustomProgressView.h
#import <UIKit/UIKit.h>
#interface CustomProgressView : UIProgressView
#end
CustomProgressView.m
#import "CustomProgressView.h"
#implementation CustomProgressView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
self.frame = CGRectMake(self.frame.origin.x, self.frame.origin.y, self.frame.size.width, 16);
UIImage *progressBarTrack = [[UIImage imageNamed:#"progressBarTrack"] resizableImageWithCapInsets:UIEdgeInsetsZero];
UIImage *progressBar = [[UIImage imageNamed:#"progressBar"] resizableImageWithCapInsets:UIEdgeInsetsMake(4, 4, 5, 4)];
[progressBarTrack drawInRect:rect];
NSInteger maximumWidth = rect.size.width - 2;
NSInteger currentWidth = floor([self progress] * maximumWidth);
CGRect fillRect = CGRectMake(rect.origin.x + 1, rect.origin.y + 1, currentWidth, 14);
[progressBar drawInRect:fillRect];
}
#end
The resulting ProgressView has the right height and width. It also fills at the right percentage (currently set at 80%). But the progress fill image isn't drawn correctly.
Does anyone see where I go wrong?
Looks like you're reassigning self.frame in -drawRect.
I think you want something like this:
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGRect bounds = self.bounds ;
UIImage *progressBarTrack = [ UIImage imageNamed:#"progressBarTrack"] ;
[ progressBarTrack drawInRect:bounds ] ;
UIImage *progressBar = [[UIImage imageNamed:#"progressBar"] resizableImageWithCapInsets:(const UIEdgeInsets){ 4.0f, 4.0f, 5.0f, 4.0f } ] ;
CGRect fillRect = CGRectInset( bounds, 2.0f, 2.0f ) ;
fillRect.width = floorf( self.progress * maximumWidth );
[progressBar drawInRect:fillRect];
}
How to create your own progress view overriding UIView instead of UIProgressView
#interface ProgressView : UIView
#property float progress ;
#end
#implementation ProgressView
#synthesize progress = _progress ;
-(id)initWithFrame:(CGRect)frame
{
if (( self = [ super initWithFrame:frame ] ))
{
self.layer.needsDisplayOnBoundsChange = YES ;
}
return self ;
}
-(void)drawRect
{
// see code above
}
-(void)setProgress:(float)progress
{
_progress = progress ;
[ self setNeedsDisplay ] ;
}
#end