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
Related
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?
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 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>
I'm doing a GPS tracking app. Every time it receives a Latitude/Longitude it converts it to (x,y) coordinates and calls drawRect to draw a line between two (x,y) pairs.
However, the drawRect method just clear all the old contents before it draw new thing. How I can make the drawRect to draw new thing on the existing canvas? Thanks in advance
here is my viewController.h
#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>
#class routetrack;
#interface simpleDrawViewController : UIViewController <CLLocationManagerDelegate> {
CLLocationManager *locationManager;
IBOutlet routetrack *routeroute;
float latOld;
float longOld;
float latNew;
float longNew;
}
- (IBAction) gpsValueChanged;
#property (nonatomic,retain) CLLocationManager *locationManager;
#property (retain,nonatomic) routetrack *routeroute;
ViewController.m
#import "simpleDrawViewController.h"
#import "routetrack.h"
#implementation simpleDrawViewController
#synthesize locationManager,btn,routeroute;
- (IBAction) gpsValueChanged{
[routeroute setLocationX:longNew setLocationY:latNew
oldLocation:longOld oldLocationY:latOld ];
[routeroute setNeedsDisplay];
}
- (void)viewDidLoad {
[super viewDidLoad];
if (nil == locationManager)
locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[locationManager startUpdatingLocation];
}
- (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 {
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void) locationManager:(CLLocationManager *)manager
didUpdateToLocation:(CLLocation *)newLocation
fromLocation:(CLLocation *)oldLocation
{
if (oldLocation) {
latOld=oldLocation.coordinate.latitude;
longOld=oldLocation.coordinate.longitude;
latNew=newLocation.coordinate.latitude;
longNew=newLocation.coordinate.longitude;
}
else {
latOld=53.3834;
longOld=-6.5876;
latNew=53.3835;
longNew=-6.5877;
}
[self gpsValueChanged];
}
- (void) locationManager:(CLLocationManager *)manager didFailwithError:
(NSError *)error
{
if([error code] == kCLErrorDenied)
[locationManager stopUpdatingLocation];
NSLog(#"location manger failed");
}
- (void)dealloc {
[locationManager release];
[super dealloc];
}
#end
my drawing class, a subclass of UIView
#import "routetrack.h"
#implementation routetrack
- (void) setLocationX:(float) loX setLocationY:(float) loY
oldLocation:(float) oldX oldLocationY:(float) oldY {
self->locationX = loX;
self->locationY = loY;
self->oldLoX = oldX;
self->oldLoY = oldY;
scaleX= 36365.484375;
scaleY= 99988.593750;
maxLat= 53.3834;
maxLog= -6.5976;
drawX = locationX;
drawY = locationY;
tempX=(maxLog - drawX) * scaleX;
tempY=(maxLat - drawY) * scaleY;
lastX = (int) tempX;
lastY = (int) tempY;
drawX = oldLoX;
drawY = oldLoY;
tempX=(maxLog - drawX) * scaleX;
tempY=(maxLat - drawY) * scaleY;
startX = (int) tempX;
startY = (int) tempY;
}
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
}
return self;
}
- (void)drawRect:(CGRect)rect {
int firstPointX = self->startX;
int firstPointY = self->startY;
int lastPointX = self->lastX;
int lastPointY = self->lastY;
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0);
CGContextSetLineWidth(context, 2.0);
CGContextMoveToPoint(context, firstPointX, firstPointY);
CGContextAddLineToPoint(context, lastPointX , lastPointY);
CGContextStrokePath(context);
}
UIView does not have a canvas model. If you want to keep a canvas, you should create a CGLayer or a CGBitmapContext and draw onto that. Then draw that in your view.
I would create an ivar for a CGLayerRef, and then drawRect: would look something like this (untested):
- (void)drawRect:(CGRect)rect {
if (self.cgLayer == NULL) {
CGLayerRef layer = CGLayerCreateWithContext(UIGraphicsContextGetCurrent(), self.bounds, NULL);
self.cgLayer = layer;
CGLayerRelease(layer);
}
... various Core Graphics calls with self.cgLayer as the context ...
CGContextDrawLayerInRect(UIGraphicsContextGetCurrent(), self.bounds, self.cgLayer);
}
Whenever you wanted to clear your canvas, just self.cgLayer = NULL.
setCgLayer: would be something like this:
- (void)setCgLayer:(CGLayerRef)aLayer {
CGLayerRetain(aLayer);
CGLayerRelease(cgLayer_);
cgLayer_ = aLayer;
}
What exactly to you mean by "old contents"? If you want to draw a line from your GPS data, you have to draw all points every time in the implementation of drawRect.
I have a great problem, I have a UILabel created programmatically and then connect via Interface builder, where i have positionated where I need, but i see that the text i set in it it's printed in the center of the UILabelBox, I have found a lot of question but i haven't know I can use it, I have found this:
//
// VerticallyAlignedLabel.h
//
#import <Foundation/Foundation.h>
typedef enum VerticalAlignment {
VerticalAlignmentTop,
VerticalAlignmentMiddle,
VerticalAlignmentBottom,
} VerticalAlignment;
#interface VerticallyAlignedLabel : UILabel {
#private
VerticalAlignment verticalAlignment_;
}
#property (nonatomic, assign) VerticalAlignment verticalAlignment;
#end
//
// VerticallyAlignedLabel.m
//
#import "VerticallyAlignedLabel.h"
#implementation VerticallyAlignedLabel
#synthesize verticalAlignment = verticalAlignment_;
- (id)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
self.verticalAlignment = VerticalAlignmentMiddle;
}
return self;
}
- (void)setVerticalAlignment:(VerticalAlignment)verticalAlignment {
verticalAlignment_ = verticalAlignment;
[self setNeedsDisplay];
}
- (CGRect)textRectForBounds:(CGRect)bounds limitedToNumberOfLines:(NSInteger)numberOfLines {
CGRect textRect = [super textRectForBounds:bounds limitedToNumberOfLines:numberOfLines];
switch (self.verticalAlignment) {
case VerticalAlignmentTop:
textRect.origin.y = bounds.origin.y;
break;
case VerticalAlignmentBottom:
textRect.origin.y = bounds.origin.y + bounds.size.height - textRect.size.height;
break;
case VerticalAlignmentMiddle:
// Fall through.
default:
textRect.origin.y = bounds.origin.y + (bounds.size.height - textRect.size.height) / 2.0;
}
return textRect;
}
-(void)drawTextInRect:(CGRect)requestedRect {
CGRect actualRect = [self textRectForBounds:requestedRect limitedToNumberOfLines:self.numberOfLines];
[super drawTextInRect:actualRect];
}
#end
Anyone can help me how I can use it please?
I know it may be late to answer, but I did it this way:
I made a Category for UILabel and call -setVerticalAlignmentTop when it was needed.
// UILabel(VAlign).h
#import <Foundation/Foundation.h>
#interface UILabel (VAlign)
- (void) setVerticalAlignmentTop;
#end
// UILabel(VAlign).m
#import "UILabel(VAlign).h"
#implementation UILabel (VAlign)
- (void) setVerticalAlignmentTop
{
CGSize textSize = [self.text sizeWithFont:self.font
constrainedToSize:self.frame.size
lineBreakMode:self.lineBreakMode];
CGRect textRect = CGRectMake(self.frame.origin.x,
self.frame.origin.y,
self.frame.size.width,
textSize.height);
[self setFrame:textRect];
[self setNeedsDisplay];
}
#end
You can do it as follows.......
Set your label's numberOfLines property 0 from IB
Set your label's lineBreakMode as UILineBreakModeWordWrap (very very important)
now whatever you set on the label just append few #"\n" to it.....
ex.-
[yourTextLabel setText:#"myLabel\n\n\n\n\n"];