I am trying to change a line stroke with UISlider, but it doesn't work. The slider value is okay but nothing is changed.
The slider is on the display and runs and give a value back on a label. I think the function is drawed on the first load and than it stops. But i don't know how fix that problem.
My .h file:
#import <UIKit/UIKit.h>
#interface Draw : UIView {
IBOutlet UISlider *slider;
IBOutlet UILabel *label;
}
- (IBAction)sliderValueChanged:(id)sender;
#end
My .m file:
#import "Draw.h"
#implementation Draw
- (IBAction) sliderValueChanged:(UISlider *)sender {
label.text = [NSString stringWithFormat:#"%.1f", slider.value];
NSLog(#"slider value = %f", sender.value);
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(CGRect)rect;
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 1.0, 0.0, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 50.0, 50.0);
CGContextAddLineToPoint(context, 250.0, 100.0);
CGContextAddLineToPoint(context, 250.0, 350.0);
CGContextAddLineToPoint(context, 50.0, 350.0);
CGContextClosePath(context);
CGContextSetLineWidth(context, slider.value );
CGContextStrokePath(context);
}
#end
In your method - (IBAction) sliderValueChanged:(UISlider *)sender you should call [self setNeedsDisplay] to inform the view that the contents need to be redrawn.
You need to tell the UI that it needs to be redrawn with the -[UIView setNeedsDisplay] message. Otherwise, it doesn't know that anything related to how the view is drawn has changed
- (IBAction) sliderValueChanged:(UISlider *)sender {
label.text = [NSString stringWithFormat:#"%.1f", slider.value];
NSLog(#"slider value = %f", sender.value);
[self setNeedsDisplay];
}
Related
I am trying to create a circular progress bar around an image as shown in the screenshot below. So far I have managed to create a round image with a green border using the code below:
self.image.layer.cornerRadius = self.image.frame.size.width / 2
self.image.clipsToBounds = true
self.image.layer.borderWidth = 6.0
self.image.layer.borderColor = UIColor.greenColor.CGColor
My question is, how do I create a circular progress bar from the border than I have set? Or do I need to remove this border and take a different approach?
I understood your problem and I suggest you to use some library to create such a loader you can find many of the swift libraries. On of them is FPActivityIndicator, it is the same circular loader which you are searching for, you only have to adjust the radius and position of the loader to move it around the image
if you need further help you can send me message and if it helps please accept the answer. Thanks
I have customized the answer of WDUK in stack overflow post according to your need,
It is something like,
TestView.h
#import <UIKit/UIKit.h>
#interface TestView : UIView
#property (nonatomic) double percent;
#end
TestView.m
#import "TestView.h"
#interface TestView () {
CGFloat startAngle;
CGFloat endAngle;
}
#end
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
self.backgroundColor = [UIColor whiteColor];
// Determine our start and stop angles for the arc (in radians)
startAngle = M_PI * 1.5;
endAngle = startAngle + (M_PI * 2);
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGFloat constant = rect.size.width/ 5;
UIImage *img = [UIImage imageNamed:#"lion.jpg"]; // lion.jpg is image name
UIImageView *imgView = [[UIImageView alloc]initWithFrame:CGRectMake(rect.origin.x + constant/2, rect.origin.y + constant/2, rect.size.width-constant, rect.size.height - constant)];
imgView.image = img;
imgView.layer.masksToBounds = YES;
imgView.layer.cornerRadius = imgView.frame.size.width / 2;
UIBezierPath* bezierPath = [UIBezierPath bezierPath];
// Create our arc, with the correct angles
[bezierPath addArcWithCenter:CGPointMake(rect.size.width / 2, rect.size.height / 2)
radius:constant*2
startAngle:startAngle
endAngle:(endAngle - startAngle) * (_percent / 100.0) + startAngle
clockwise:YES];
// Set the display for the path, and stroke it
bezierPath.lineWidth = 20;
[[UIColor redColor] setStroke];
[bezierPath stroke];
[self addSubview:imgView];
}
#end
ViewController.m
#import "ViewController.h"
#import "TestView.h"
#interface ViewController (){
TestView* m_testView;
NSTimer* m_timer;
}
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Init our view
CGRect frame = CGRectMake(50, 50, 200, 200);
m_testView = [[TestView alloc] initWithFrame:frame];
m_testView.percent = 0;
[self.view addSubview:m_testView];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)viewDidAppear:(BOOL)animated
{
// Kick off a timer to count it down
m_timer = [NSTimer scheduledTimerWithTimeInterval:0.1 target:self selector:#selector(increamentSpin) userInfo:nil repeats:YES];
}
- (void)increamentSpin
{
// increament our percentage, do so, and redraw the view
if (m_testView.percent < 100) {
m_testView.percent = m_testView.percent + 1;
[m_testView setNeedsDisplay];
}
else {
[m_timer invalidate];
m_timer = nil;
}
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
If you are decreasing frame size from viewcontroller then you should reduce bezierPath.lineWidth accordingly to show respectively thin progress around imageview.
And this is working perfact, i have tested it!!!
I'm trying to do a UIView category for drawing inside drawRect: method without subclassing. To do this, I created a block to simplify this task.
Here's the code:
UIView+DrawRectBlock.h
#import <UIKit/UIKit.h>
// DrawRect block
typedef void(^DrawRectBlock)(UIView *drawRectView, CGRect rect);
#interface UIView (DrawRectBlock)
- (void)drawInside:(DrawRectBlock)block;
#end
UIView+DrawRectBlock.m
#import "UIView+DrawRectBlock.h"
#import <objc/runtime.h>
#interface UIView ()
#pragma mark - Private properties
#property DrawRectBlock drawBlock;
#end
#implementation UIView (DrawRectBlock)
- (void)drawInside:(DrawRectBlock)block {
if ((self.drawBlock = [block copy])) {
[self setNeedsDisplay];
}
}
- (void)drawRect:(CGRect)rect {
if (self.drawBlock) {
self.drawBlock(self, rect);
}
}
- (void)dealloc {
self.drawBlock = nil;
}
#pragma mark - Others
- (void)setDrawBlock:(DrawRectBlock)drawBlock {
objc_setAssociatedObject(self, #"block", drawBlock, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (DrawRectBlock)drawBlock {
return objc_getAssociatedObject(self, #"block");
}
#end
Finally, I call block as follows:
[_testView drawInside:^(UIView *drawRectView, CGRect rect) {
NSLog(#"DrawReeeeeeect!!!!");
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [UIColor redColor].CGColor);
CGContextSetLineWidth(context, 5.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, 0.0, 0.0); //start at this point
CGContextAddLineToPoint(context, 100.0, 100.0); //draw to this point
CGContextStrokePath(context);
}];
But "drawRect:" is never called.
Any idea?
Thanks!!
Finally I made a mix of subclass and category and works great!
https://github.com/mhergon/SimpleBlockDrawing
The new ios 7 phone app has a favorites section. In that section the names of the contact appear next to a filled in circle with the inital of the contact inside the circle.
How is this drawn? With drawrect or is there already and object created for this?
Below is a UIView subclass that will do what you want. It will correctly size and position 1 or more letters in the circle. Here's how it looks with 1-3 letters at various sizes (32, 64, 128, 256):
With the availability of user defined runtime attributes in Interface Builder, you can even configure the view from within IB. Just set the text property as a runtime attribute and the backgroundColor to the color you want for the circle.
Here's the code:
#interface MELetterCircleView : UIView
/**
* The text to display in the view. This should be limited to
* just a few characters.
*/
#property (nonatomic, strong) NSString *text;
#end
#interface MELetterCircleView ()
#property (nonatomic, strong) UIColor *circleColor;
#end
#implementation MELetterCircleView
- (instancetype)initWithFrame:(CGRect)frame text:(NSString *)text
{
NSParameterAssert(text);
self = [super initWithFrame:frame];
if (self)
{
self.text = text;
}
return self;
}
// Override to set the circle's background color.
// The view's background will always be clear.
-(void)setBackgroundColor:(UIColor *)backgroundColor
{
self.circleColor = backgroundColor;
[super setBackgroundColor:[UIColor clearColor]];
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[self.circleColor setFill];
CGContextAddArc(context, CGRectGetMidX(rect), CGRectGetMidY(rect),
CGRectGetWidth(rect)/2, 0, 2*M_PI, YES);
CGContextFillPath(context);
[self drawSubtractedText:self.text inRect:rect inContext:context];
}
- (void)drawSubtractedText:(NSString *)text inRect:(CGRect)rect
inContext:(CGContextRef)context
{
CGContextSaveGState(context);
// Magic blend mode
CGContextSetBlendMode(context, kCGBlendModeDestinationOut);
CGFloat pointSize =
[self optimumFontSizeForFont:[UIFont boldSystemFontOfSize:100.f]
inRect:rect
withText:text];
UIFont *font = [UIFont boldSystemFontOfSize:pointSize];
// Move drawing start point for centering label.
CGContextTranslateCTM(context, 0,
(CGRectGetMidY(rect) - (font.lineHeight/2)));
CGRect frame = CGRectMake(0, 0, CGRectGetWidth(rect), font.lineHeight)];
UILabel *label = [[UILabel alloc] initWithFrame:frame];
label.font = font;
label.text = text;
label.textAlignment = NSTextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[label.layer drawInContext:context];
// Restore the state of other drawing operations
CGContextRestoreGState(context);
}
-(CGFloat)optimumFontSizeForFont:(UIFont *)font inRect:(CGRect)rect
withText:(NSString *)text
{
// For current font point size, calculate points per pixel
CGFloat pointsPerPixel = font.lineHeight / font.pointSize;
// Scale up point size for the height of the label.
// This represents the optimum size of a single letter.
CGFloat desiredPointSize = rect.size.height * pointsPerPixel;
if ([text length] == 1)
{
// In the case of a single letter, we need to scale back a bit
// to take into account the circle curve.
// We could calculate the inner square of the circle,
// but this is a good approximation.
desiredPointSize = .80*desiredPointSize;
}
else
{
// More than a single letter. Let's make room for more.
desiredPointSize = desiredPointSize / [text length];
}
return desiredPointSize;
}
#end
I made a Custom Circle Control, Were i am Sending values by Slider from my RootView Controller. Can You help me out, how to Apply touches to Circle Control were i can change the values, like making custom slider around my Custom Circle.
#import "RKCustomCircle.h"
#implementation RKCustomCircle
#synthesize sliderPerccentageValue;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
xPos = 320/2;
yPos = 250;
radius = 80;
rotationAngle = 0;
}
return self;
}
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
[self drawCircleChart: context];
}
- (void) drawCircleChart:(CGContextRef) context
{
CGContextSetFillColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextFillRect(context, CGRectMake(0, 0, 320, 480));
float a = rotationAngle;
[self drawCirclewithStartingAngle:a withContext:context];
}
- (void) drawCirclewithStartingAngle:(float)startAngle withContext:(CGContextRef) context
{
float endAngle = startAngle + (sliderPerccentageValue/ 100) * (M_PI*2);
float adjY = yPos;
float rad = radius;
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextSetStrokeColorWithColor(context,[UIColor blackColor].CGColor);
CGContextSetLineWidth(context, 1.0);
CGContextBeginPath(context);
CGContextMoveToPoint(context, xPos, adjY);
CGContextAddArc(context, xPos, adjY, rad, startAngle, endAngle, 0);
CGContextClosePath(context);
CGContextDrawPath(context, kCGPathFillStroke);
}
RootView is,
#implementation RKViewController
- (void)viewDidLoad
{
[super viewDidLoad];
circleView = [[RKCustomCircle alloc] initWithFrame:CGRectMake(0, 100, 320, 360)];
[circleView addTarget:self action:#selector(newValue:) forControlEvents:UIControlEventValueChanged];
[self.view addSubview:circleView];
}
- (IBAction)sliderValueChangedInPercentage:(UISlider *)sender {
circleView.sliderPerccentageValue =sender.value;
[circleView setNeedsDisplay];
}
Use UITapGestureRecognizer.
UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:self action:#selector(oneTap:)];
[singleTap setNumberOfTapsRequired:1];
[singleTap setNumberOfTouchesRequired:1];
[circleView addGestureRecognizer:singleTap];
Add this method
- (void)oneTap:(UIGestureRecognizer *)gesture
{
NSLog(#"Touch occur");
}
All your things are correct, but only the following part is incorrect.
- (IBAction)sliderValueChangedInPercentage:(UISlider *)sender {
circleView.sliderPerccentageValue =sender.value;
[circleView setNeedsDisplay];
}
The above code doesn't supply any value because you have attached to your control that implements UIControl, not UISlider. You don't have to either, your UIControl will work..
Instead you can have touch events inside your control itself no need to add any action from outside, and you have to detect circular touch and keep track of values, see this app here it will help you to get values when touched circular.
Hope it will help you. All the best.
You need to override the pointInside:withEvent: method of the circle view. Refer to this for more information.
I have custom view Chart and view controller for that view GraphViewController.
I'm implementing simple graph.
Here is code for Chart.h
#import
#interface Chart : UIView {
BOOL hideChart;
}
#property BOOL hideChart;
- (void) drawAxesInContext:(CGContextRef)context;
- (void) drawUserChartinContext:(CGContextRef)context;
#end
Chart.m
- (void)drawRect:(CGRect)rect
{
CGContextRef ctxt = UIGraphicsGetCurrentContext();
[self drawAxesInContext:ctxt];
[self drawUserChartinContext:ctxt];
}
- (void) drawAxesInContext:(CGContextRef)context {
CGContextTranslateCTM(context, nullX, nullY);
CGContextScaleCTM(context, 1.0, -1.0);
CGContextSetLineWidth(context, 3);
CGContextBeginPath(context);
for (int i=0; i
As you can see on screenshot I have uibutton "hide". I want to hide graph (not axes) by pressing this button.
In viewController I created ibaction
- (IBAction) hideAndShow {
self.chart.hideChart = YES;
}
and in view u can see
if (hideChart) {
[[UIColor blackColor] setStroke];
[self setNeedsDisplay];
NSLog(#"hide");
}
But it not works, do you any ideas how can I make this?
http://dl.dropbox.com/u/866144/Voila_Capture7.png
I suggest adding a custom setter for hideChart, something like this
- (void)setHideChart:(BOOL)isHidden {
self.isHidden = isHidden;
}