so I created a subview on my project and inside that subview, I want to draw just a simple text. I create a subview on my Main Story Board and reference it to my CustomView. Set its size equal to my custom view size. Here is the my snippet code.
The custom view does show up as red color, like I set how it display, however, the text inside it ("Hello world") doesn't show up. I've been looking at the code for a while now and could not figure out what I did wrong. I really need help. Would someone tell me what to do ? Thank you.
ViewController.h
#interface ViewController : UIViewController
#property(retain, nonatomic) UIView *subView;
#end
ViewController.m
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
CustomView.h
#interface CustomView : UIView
#property(retain, nonatomic)IBOutlet UIView* view;
#end
CustomView.m
#implementation CustomView
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect)rect {
// Drawing code
// how to draw text in here
[super drawRect:rect];
// Initialize a graphics context in IOS
CGContextRef context = UIGraphicsGetCurrentContext();
// Flip the coordinate
CGContextTranslateCTM(context, 0, self.bounds.size.height);
CGContextScaleCTM(context, 1.0, -1.0);
// ???????
// Initialize string
CFStringRef string = CFSTR("Hello world");
// Initialize font
CTFontRef font = CTFontCreateWithName((CFStringRef)#"Helvetica", 16.0f, nil);
CFStringRef keys[] = {kCTFontAttributeName};
CFTypeRef values[] = {font};
CFDictionaryRef attributes = CFDictionaryCreate(kCFAllocatorDefault, (const void **) &keys, (const void **) &values, sizeof(keys) / sizeof(keys[0]), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFAttributedStringRef attrString = CFAttributedStringCreate(kCFAllocatorDefault, string, attributes);
CFRelease(string);
CFRelease(attributes);
CTLineRef line = CTLineCreateWithAttributedString(attrString);
// Set text position and draw the line into the graphics context
CGContextSetTextPosition(context, 10, 10);
CTLineDraw(line, context);
CFRelease(line);
}
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if(self)
{
// 1. load the .xib file
[[NSBundle mainBundle] loadNibNamed:#"CustomView" owner:self options:nil];
// 2. add the subview
self.view.backgroundColor = [UIColor redColor];
[self addSubview:self.view];
}
return self;
}
#end
I tried your code, except using xibs and it works perfect. Btw, where did you placed your customView in your viewController.view?
Related
I am creating progress view by using CALayer by following
Header class
#import <UIKit/UIKit.h>
#import <QuartzCore/QuartzCore.h>
#interface ProgressBar : UIView
#property (strong, nonatomic) CALayer *subLayer;
#property (nonatomic) CGFloat progress;
#end
Implementation class
#implementation ProgressBar
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.progress = 0;
self.backgroundColor = [UIColor whiteColor];
self.subLayer = [CALayer layer];
self.subLayer.frame = self.bounds;
self.subLayer.backgroundColor = [UIColor orangeColor].CGColor;
[self.layer addSublayer:self.subLayer];
}
return self;
}
- (void)setProgress:(CGFloat)value {
NSLog(#"what is going on here");
if (self.progress != value) {
self.progress = value;
[self setNeedsLayout];
}
}
- (void)layoutSubviews {
CGRect rect = [self.subLayer frame];
rect.size.width = CGRectGetWidth([self bounds]) * self.progress;
[self.subLayer setFrame:rect];
}
In my viewcontroller, I am doing
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
self.pBar = [[ProgressBar alloc] initWithFrame:CGRectMake(0, 50, 320, 10)];
[self.view addSubview:self.pBar];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
- (IBAction)click:(id)sender {
[self.pBar setProgress:.8];
}
When (IBAction)click is fired, I am getting into the infinitive loop like the picture below
Does anyone know what I am getting into this loop. Please advice me on this issue. Thanks
The problem is that your setProgress: method calls the setProgress: method - recursion.
- (void)setProgress:(CGFloat)value {
NSLog(#"what is going on here");
if (self.progress != value) {
self.progress = value; // <-- This calls [self setProgress:value]
[self setNeedsLayout];
}
}
Change the bad line to:
_progress = value;
Setting the ivar directly is always required when you implement the setter method of a property.
I have UITextView and want to underline selected text.
It is working by this code
NSRange range = selectedTextView.selectedRange;
NSTextStorage *textStorage = selectedTextView.textStorage;
[textStorage addAttribute: NSUnderlineStyleAttributeName
value:[NSNumber numberWithInt:NSUnderlineStyleSingle]
range:range];
But letters which have "a tail" under baseline (as q y g p) are not underlined (screenshot: http://i.stack.imgur.com/dRrEH.png).
Also I noticed that space between baseline and underline depends on font.
How can I underline text without any breakings/spacing?
#import <UIKit/UIKit.h>
#interface TextViewWithUnderline : UITextView
#end
#import "TextViewWithUnderline.h"
#implementation TextViewWithUnderline
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
//self.contentMode = UIViewContentModeRedraw;
}
return self;
}
- (void)drawRect:(CGRect)rect {
//Get the current drawing context
CGContextRef context = UIGraphicsGetCurrentContext();
//Set the line color and width
CGContextSetStrokeColorWithColor(context, [UIColor lightGrayColor].CGColor);
CGContextSetLineWidth(context, 1.0f);
//Start a new Path
CGContextBeginPath(context);
//Find the number of lines in our textView + add a bit more height to draw lines in the empty part of the view
NSUInteger numberOfLines = (self.contentSize.height + self.bounds.size.height) / self.font.leading;
//Set the line offset from the baseline. (I'm sure there's a concrete way to calculate this.)
CGFloat baselineOffset = 6.0f;
//iterate over numberOfLines and draw each line
for (int x = 1; x < numberOfLines; x++) {
//0.5f offset lines up line with pixel boundary
CGContextMoveToPoint(context, self.bounds.origin.x + 10, self.font.leading*x + 0.5f + baselineOffset);
CGContextAddLineToPoint(context, self.bounds.size.width - 10, self.font.leading*x + 0.5f + baselineOffset);
}
//Close our Path and Stroke (draw) it
CGContextClosePath(context);
CGContextStrokePath(context);
}
#end
#import <UIKit/UIKit.h>
#import "TextViewWithUnderline.h"
#interface CustomTextView : UIViewController
#property (weak, nonatomic) IBOutlet TextViewWithUnderline *textView;
#end
#import "CustomTextView.h"
#interface CustomTextView ()
#end
#implementation CustomTextView
- (id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
// Custom initialization
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view.
self.textView.contentMode = UIViewContentModeRedraw;
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Why is my color property always null?
#interface StreamingMediaControlButton : UIButton
#property (strong, nonatomic) UIColor *color;
#end
#implementation StreamingMediaControlButton
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
_color = [UIColor whiteColor];
}
return self;
}
- (void)drawRect:(CGRect)rect
{
NSLog(#"color: %#", self.color);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetFillColorWithColor(context, self.color.CGColor);
}
#end
Your previous comment said you are using interface builder, which means a different init method is invoked on the button. You need to implement:
- (id)initWithCoder:(NSCoder *)decoder
{
if ((self = [super initWithCoder:decoder])) {
// setup code
}
return self;
}
Quite often it's good practice to implement both initWithCoder: and initWithFrame: and have them both call a commonInit method where your shared setup code goes.
Try to Use Color RGB value instead of directly defining color that would be more effective.
I want to write a custom UIView that does the following:
It takes an image and adds it to a view.
It has a method to flip that image.
I want to pass this Custom UIView to the iCarousel class:
https://github.com/nicklockwood/iCarousel
How can I create a custom UIView using Objective C and Cocoa?
I started by doing the following:
CItem.h
#import <UIKit/UIKit.h>
#interface CItem : UIView
{
UIImageView *box;
}
#property (nonatomic, retain) UIImageView * box;
#end
CItem.m
#import "CItem.h"
#implementation CItem
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)drawRect:(CGRect)rect {
// Drawing code
box = [[UIImageView alloc] initWithFrame:CGRectMake(0,0, 240, 240)];
[box setImage:[UIImage imageNamed:# "cleaning.png"]];
[self addSubview:box];
}
#end
You should not add your addSubview: in drawRect:. See this method's disscussion:
Discussion
The default implementation of this method does nothing.
Subclasses that use native drawing technologies (such as Core Graphics
and UIKit) to draw their view’s content should override this method
and implement their drawing code there. You do not need to override
this method if your view sets its content in other ways. For example,
you do not need to override this method if your view just displays a
background color or if your view sets its content directly using the
underlying layer object. Similarly, you should not override this
method if your view uses OpenGL ES to do its drawing.
If you don't use a xib file for your CItem you can add your code to initWithFrame:.
//CItem.h
#import <UIKit/UIKit.h>
#interface CItem : UIView
- (void)flip;
#end
// CItem.m
#import "CItem.h"
#interface CItem()
#property (assign, nonatomic) BOOL displayingPrimary;
#property (strong, nonatomic) UIImageView *primaryView;
#property (strong, nonatomic) UIImageView *secondaryView;
#end
#implementation CItem
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
_primaryView = [[UIImageView alloc] initWithFrame:frame];
[_primaryView setImage:[UIImage imageNamed:# "cleaning.jpg"]];
[self addSubview:_primaryView];
_secondaryView = [[UIImageView alloc] initWithFrame:frame];
[_secondaryView setImage:[UIImage imageNamed:# "adding.jpg"]];
[self addSubview:_secondaryView];
}
return self;
}
- (void)flip
{
[UIView transitionFromView:(self.displayingPrimary ? self.primaryView : self.secondaryView)
toView:(self.displayingPrimary ? self.secondaryView : self.primaryView)
duration:1.0
options:(self.displayingPrimary ? UIViewAnimationOptionTransitionFlipFromRight :
UIViewAnimationOptionTransitionFlipFromLeft) | UIViewAnimationOptionShowHideTransitionViews
completion:^(BOOL finished) {
if (finished) {
self.displayingPrimary = !self.displayingPrimary;
}
}];
}
#end
Then you can use CItem like this:
CItem *subView = [[CItem alloc] initWithFrame:CGRectMake(0, 0, 320, 400)];
[self.view addSubview:subView];
You should only implement drawRect if you intend to do custom drawing. If you want to use subviews you can just move your code into the initWithFrame:
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
box = [[UIImageView alloc] initWithFrame:CGRectMake(0.0f, 0.0f, 240.0f, 240.0f)];
[box setImage:[UIImage imageNamed:#"cleaning"]];
[self addSubview:box];
}
return self;
}
You can safely remove the drawRect method. Not sure what you mean by "flipping" the image, but you should be able to manipulate the imageView object using the "box" property of your custom class.
I want to draw the text in an UITextField with a shadow. In order to do this, I have subclassed UITextField, and implemented the drawTextInRect: method as follows:
- (void)drawTextInRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
// Create shadow color
float colorValues[] = {0.21875, 0.21875, 0.21875, 1.0};
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGColorRef shadowColor = CGColorCreate(colorSpace, colorValues);
CGColorSpaceRelease(colorSpace);
// Create shadow
CGSize shadowOffset = CGSizeMake(2, 2);
CGContextSetShadowWithColor(context, shadowOffset, 0, shadowColor);
CGColorRelease(shadowColor);
// Render text
[super drawTextInRect:rect];
}
This works great for when the text field is not editing, but as soon as editing begins, the shadow disappears. Is there anything I am missing?
Here is the code for following component
#interface AZTextField ()
- (void)privateInitialization;
#end
#implementation AZTextField
static CGFloat const kAZTextFieldCornerRadius = 3.0;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (!self) return nil;
[self privateInitialization];
return self;
}
// In case you decided to use it in a nib
- (id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (!self) return nil;
[self privateInitialization];
return self;
}
- (void)privateInitialization
{
self.borderStyle = UITextBorderStyleNone;
self.layer.masksToBounds = NO;
self.layer.shadowColor = [UIColor blackColor].CGColor;
self.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
self.layer.shadowOpacity = 0.5f;
self.layer.backgroundColor = [UIColor whiteColor].CGColor;
self.layer.cornerRadius = 4;
// This code is better to be called whenever size of the textfield changed,
// so if you plan to do that you can add an observer for bounds property
UIBezierPath *shadowPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds cornerRadius:kAZTextFieldCornerRadius];
self.layer.shadowPath = shadowPath.CGPath;
}
#end
Couple of things to consider:
You want to set borderStyle to none, otherwise you'll end up with
UIKit putting subviews into your textfield
Depending on the Xcode
version you might want to link QuartzCore and to #import
<QuartzCore/QuartzCore.h>
For more complex appearance you can still
use shadow properties of the layer and move the drawing code itself
into the drawRect: method, but jake_hetfield was right if you
override drawRect you don't want to call super, especially in the end
of the method
As for the text drawing (you can see that it sticks to
close to the component borders), you have a separate
drawTextInRect: and drawPlaceholderInRect: method that draws the
text and placeholder respectively
You can use UIColor method for
colors and call CGColor property, it makes code more readable and
easier to maintain
Hope that helps!
Inspired by #jake_hetfield answer I created a custom UITextField that uses an internal label to do the drawing, check it out:
ShadowTextField .h file
#import <UIKit/UIKit.h>
#interface ShadowTextField : UITextField
// properties to change the shadow color & offset
#property (nonatomic, retain) UIColor *textShadowColor;
#property (nonatomic) CGSize textShadowOffset;
- (id)initWithFrame:(CGRect)frame
font:(UIFont *)font
textColor:(UIColor *)textColor
shadowColor:(UIColor *)shadowColor
shadowOffset:(CGSize)shadowOffset;
#end
ShadowTextField .m file
#import "ShadowTextField.h"
#interface ShadowTextField ()
#property (nonatomic, retain) UILabel *internalLabel;
#end
#implementation ShadowTextField
#synthesize internalLabel = _internalLabel;
#synthesize textShadowColor = _textShadowColor;
#synthesize textShadowOffset = _textShadowOffset;
- (id)initWithFrame:(CGRect)frame
font:(UIFont *)font
textColor:(UIColor *)textColor
shadowColor:(UIColor *)shadowColor
shadowOffset:(CGSize)shadowOffset
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
// register to my own text changes notification, so I can update the internal label
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(handleUITextFieldTextDidChangeNotification)
name:UITextFieldTextDidChangeNotification
object:nil];
self.font = font;
self.textColor = textColor;
self.textShadowColor = shadowColor;
self.textShadowOffset = shadowOffset;
}
return self;
}
// when the user enter text we update the internal label
- (void)handleUITextFieldTextDidChangeNotification
{
self.internalLabel.text = self.text;
[self.internalLabel sizeToFit];
}
// init the internal label when first needed
- (UILabel *)internalLabel
{
if (!_internalLabel) {
_internalLabel = [[UILabel alloc] initWithFrame:self.bounds];
[self addSubview:_internalLabel];
_internalLabel.font = self.font;
_internalLabel.backgroundColor = [UIColor clearColor];
}
return _internalLabel;
}
// override this method to update the internal label color
// and to set the original label to clear so we wont get two labels
- (void)setTextColor:(UIColor *)textColor
{
[super setTextColor:[UIColor clearColor]];
self.internalLabel.textColor = textColor;
}
// override this method to update the internal label text
- (void)setText:(NSString *)text
{
[super setText:text];
self.internalLabel.text = self.text;
[self.internalLabel sizeToFit];
}
- (void)setTextShadowColor:(UIColor *)textShadowColor
{
self.internalLabel.shadowColor = textShadowColor;
}
- (void)setTextShadowOffset:(CGSize)textShadowOffset
{
self.internalLabel.shadowOffset = textShadowOffset;
}
- (void)drawTextInRect:(CGRect)rect {
// don't draw anything
// we have the internal label for that...
}
- (void)dealloc {
[_internalLabel release];
[_textShadowColor release];
[super dealloc];
}
#end
Here is how you use it in your view controller
- (void)viewDidLoad
{
[super viewDidLoad];
ShadowTextField *textField = [[ShadowTextField alloc] initWithFrame:CGRectMake(0, 0, 320, 30)
font:[UIFont systemFontOfSize:22.0]
textColor:[UIColor whiteColor]
shadowColor:[UIColor redColor]
shadowOffset:CGSizeMake(0, 1) ] ;
textField.text = #"This is some text";
textField.backgroundColor = [UIColor blackColor];
[self.view addSubview:textField];
}
You could try to do the drawing of the label yourself. Remove
[super drawTextInRect:rect]
And instead draw your own label. I haven't tried this but it could look something like this:
// Declare a label as a member in your class in the .h file and a property for it:
UILabel *textFieldLabel;
#property (nonatomic, retain) UILabel *textFieldLabel;
// Draw the label
- (void)drawTextInRect:(CGRect)rect {
if (self.textFieldLabel == nil) {
self.textFieldLabel = [[[UILabel alloc] initWithFrame:rect] autorelease];
[self.view addSubview:myLabel];
}
self.textFieldLabel.frame = rect;
self.textFieldLabel.text = self.text;
/** Set the style you wish for your label here **/
self.textFieldLabel.shadowColor = [UIColor grayColor];
self.textFieldLabel.shadowOffset = CGSizeMake(2,2);
self.textFieldLabel.textColor = [UIColor blueColor];
// Do not call [super drawTextInRect:myLabel] method if drawing your own text
}
Stop calling super and render the text yourself.
Have your tried with the standard shadow properties of the CALayer? it usually is enough and it is lot simpler. Try something like this with a regular UITextField:
self.inputContainer.layer.shadowColor=[UIColor blackColor].CGColor;
self.inputContainer.layer.shadowRadius=8.0f;
self.inputContainer.layer.cornerRadius=8.0f;
self.inputContainer.layer.shadowOffset=CGSizeMake(0, 4);
You need to import QuartzCore first of course!
#import <QuartzCore/QuartzCore.h>