Custom drawing invisible until quit - ios

In my subclass of UIView, custom drawing. The original drawing works fine, i.e. when triggered from viewDidLoad or else from a swipe or shake gesture, which clears the screen and redraws.
The problem comes when I try to draw on top of what's already there-- it behaves erratically, usually invisible and offset from where I've placed it, but then it appears if I press the home button. On home-button press, as the view disappears I get a glimpse of what I've tried to draw. If I relaunch the app all the drawing is there. What gives?
Here's how I'm set up:
- (void)drawRect:(CGRect)rect; {
cgRect = [[UIScreen mainScreen] bounds];
cgSize = cgRect.size;
context = UIGraphicsGetCurrentContext();
for (int i=0; i<(cgSize.width / spacing); i++) {
for (int j=0; j<(cgSize.height / spacing); j++) {
[self drawObjectAtPosX:i*spacing andY:j*spacing withId:i*(cgSize.height / spacing) + j];
}
}
There are invisible buttons over certain objects. When they are pressed, the object is to be redrawn in a different color:
-(void)buttonPressed:theButton {
int which = [theButton tag];
if ([[objectArray objectAtIndex:which] shouldBeRedrawn]) {
[self redrawObjectforID:which];
}
}
-(void)redrawObjectforID:(int)which {
myObject *chosenOne = [objectArray objectAtIndex:which];
CGFloat m_x = chosenOne.x;
CGFloat m_y = chosenOne.y;
context = UIGraphicsGetCurrentContext();
CGContextSetRGBStrokeColor(context, 205/255.0,173/255.0,0.0,1.0); // LINE color
CGContextSetRGBFillColor(context, 205/255.0,197/255.0,0.0,1.0); // FILL color
CGContextTranslateCTM(context, m_x, m_y);
CGContextRotateCTM(context, chosenOne.rotation);
for (int i=0; i<4; i++) {
[self drawObjectSegmentatX:m_x andY:m_y forID:which inContext:context]; // custom drawing stuff
CGContextRotateCTM(context, radians(90));
}
CGContextRotateCTM(context, -chosenOne.rotation);
CGContextTranslateCTM(context, -m_x, -m_y);
}

Answering my own, but here's what I learned today:
All custom drawing needs to be in DrawRect. The glitchy behavior I was seeing was the result of DrawRect being called by the view disappearing or being reinstated. Moved, and it works.
Location of the custom redraw is still off, but that's another question.

Related

UIViews within UIView not being drawn

I'm programatically creating a grid of UIViews for a tile based game. The trouble is that, in a three by three grid, only the centre tile is getting drawn at initialisation. The other tiles show up when they're tapped on - but not until (which isn't very good) - and changes to the tile at other times often don't appear. I've adapted my code from Mac OS NSViews (which works very well).
In the subclass for the 'grid' UIView (i.e. the container for the 'tile' UIViews):
- (void)renewRows:(NSInteger)rows columns:(NSInteger)columns {
for (int i=0; i<rows; i++) {
for (int j=0; j<columns; j++) {
CGRect tileRect;
tileRect.origin.x = j*tileSize.width;
tileRect.origin.y = i*tileSize.height;
tileRect.size.width = tileSize.width;
tileRect.size.height = tileSize.height;
id tileView = [[tilePrototype alloc]initWithFrame:tileRect patternNumber:arc4random_uniform(3) + 1];
//add touch recogniser. Will need additional recognisers for left and right swipe
UITapGestureRecognizer *singleFingerTap =
[[UITapGestureRecognizer alloc] initWithTarget:self
action:#selector(tapTile:)];
[tileView addGestureRecognizer:singleFingerTap];
[self addSubview:tileView];
}
}
}
Quite apart from the fact that the tiles show (and animate) correctly when they're tapped, I can see that the tiles are being created in the right place by stepping through this code in debug.
In my Tile UIView I have a drawRect as follows:
- (void)drawRect:(CGRect)dirtyRect {
NSLog(#"%f %f %f %f", dirtyRect.origin.x, dirtyRect.origin.y, dirtyRect.size.width, dirtyRect.size.height);
CGContextRef context = UIGraphicsGetCurrentContext();
_grid = (YANGrid *)[self superview];
double tileWidth = dirtyRect.size.width;
if (_flags.isRotating || _flags.iscentrenode ) {
CGRect centredSquare = CGRectMake(-(tileWidth/4), -(tileWidth/4), tileWidth/2, tileWidth/2);
[_backgroundColor set];
UIRectFill(dirtyRect);
CGContextSaveGState(context);
[self drawPatternConnections];
// Render Nodes
if (_flags.iscentrenode) {
[PatternGraphics centrenodeInRect:&centredSquare];
} else if (_flags.deviceType) {
[PatternGraphics outletnodeInRect:&centredSquare turnedOn:(_flags.isPowered && !_flags.isRotating)];
}
CGContextRestoreGState(context);
}
}
Can anyone here see if I'm missing anything - why aren't my tile UIViews being drawn correctly!

Adding UIImage's as "Tick Marks" to UISlider

To sum up my question beforehand: I'm trying to determine where on the slider I can place the image based upon knowing only the UISlider's duration, and having an array of times to loop through, placing the images accordingly.
I've been reading through the Apple Docs on UISlider, and it appears that there is no native way to add "Tick marks" on a UISlider based upon an array of floats. "Tick marks" meaning lines upon a slider, such as those used to place advertisements on scrubbers. Here is a visualization:
Now, I have an array full of floats; Floats in which I will use to drop the tick marks based upon the UISlider's value. The values of the floats in the array will be different every time. I would like to loop through the .value property of my UISlider, dropping the UIImages accordingly. The UIImage's are the tick marks that are just little png's assets I created. What I cannot figure out is the logic behind looping through the .value property of the UISlider and placing the UIImage in accordance with the UISlider's future position. The values of the floats in the array will be different every time, so I can't place them statically. Does anyone know where to start? I'm still a little new to Objective-C programming.
I know that it may be possible utilize retrieving the slider's beginning X coordinate on the screen, like so:
- (float)xPositionFromSliderValue:(UISlider *)aSlider;
{
float sliderRange = aSlider.frame.size.width - aSlider.currentThumbImage.size.width;
float sliderOrigin = aSlider.frame.origin.x + (aSlider.currentThumbImage.size.width / 2.0);
float sliderValueToPixels = (((aSlider.value-aSlider.minimumValue)/(aSlider.maximumValue-aSlider.minimumValu‌​e)) * sliderRange) + sliderOrigin);
return sliderValueToPixels;
}
Maybe I could add in a calculation in the for loop to place the image in accordance to that instead. I'm just not too sure where even to begin here...
The methods trackRectForBounds and thumbRectForBounds are provided for subclassing UISlider, but you can call them directly, and they will get your tick centers up front.
- (float)sliderThumbCenter:(UISlider *)slider forValue:(float)value{
CGRect trackRect = [slider trackRectForBounds:slider.bounds];
CGRect thumbRect = [slider thumbRectForBounds:slider.bounds trackRect:trackRect value:value];
CGFloat centerThumb = CGRectGetMidX(thumbRect);
return centerThumb;
}
And it might be easier to do a custom view to draw the track rather than Image views, then just put the slider on top of it and hide the track. Just make the slider frame equal to the TickView's bounds. Really I suppose a UISlider subclass would be better, but this works!
#interface TickView : UIView
#property UIColor *tickColor;
#property int tickCount;
#property CGFloat tickHeight;
#property (weak) UISlider *slider;
#property float *ticks;
-(void)setTicks:(float *)ticks count:(int)tickCount;
#end
#implementation TickView{
__weak UISlider *_slider;
}
-(instancetype)initWithFrame:(CGRect)frame{
self = [super initWithFrame:frame];
if (self) {
self.tickColor = [UIColor grayColor];
self.backgroundColor = [UIColor clearColor];
self.tickCount = 7;
self.ticks = malloc(sizeof(float) * self.tickCount);
self.tickHeight = 10;
}
return self;
}
- (void)drawRect:(CGRect)rect {
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, self.tickColor.CGColor);
CGContextBeginPath(context);
CGFloat centerY = rect.size.height / 2;
CGContextMoveToPoint(context, 0, centerY);
CGContextAddLineToPoint(context, rect.size.width, centerY);
CGFloat tickTop = centerY - self.tickHeight / 2;
CGFloat tickBottom = centerY + self.tickHeight / 2;
CGFloat tickX = 0;
if (self.slider) {
for (int i = 0; i < self.tickCount; i++) {
tickX = [self sliderThumbCenter:self.slider forValue:self.ticks[i]];
CGContextMoveToPoint(context, tickX, tickTop);
CGContextAddLineToPoint(context, tickX, tickBottom);
}
}
else{
CGFloat tickSpacing = rect.size.width / (self.tickCount - 1);
for (int i = 0; i < self.tickCount; i++) {
CGContextMoveToPoint(context, tickX, tickTop);
CGContextAddLineToPoint(context, tickX, tickBottom);
tickX += tickSpacing;
}
}
CGContextStrokePath(context);
}
-(void)setTicks:(float *)ticks count:(int)tickCount{
free(_ticks);
_ticks = malloc(sizeof(float) * tickCount);
memcpy(_ticks, ticks, sizeof(float) * tickCount);
_tickCount = tickCount;
[self setNeedsDisplay];
}
- (float)sliderThumbCenter:(UISlider *)slider forValue:(float)value{
CGRect trackRect = [slider trackRectForBounds:slider.bounds];
CGRect thumbRect = [slider thumbRectForBounds:slider.bounds trackRect:trackRect value:value];
CGFloat centerThumb = CGRectGetMidX(thumbRect);
return centerThumb;
}
-(void)setSlider:(UISlider *)slider{
_slider = slider;
}
-(UISlider *)slider{
return _slider;
}
-(void)dealloc{
free(_ticks);
}
#end
I think you will have trouble positioning the tick marks. However, if the parent view of your UISlider is "view", you add a subview like this:
[view addSubView:myTickView];
The position of the added subview is determined by its frame property, which is in the parent's view coordinate space.
To remove a view, you do this:
[myTickView removeFromSuperView];
You can also loop through your tick views and change there frames, but these changes will be animated, so the ticks will appear to slide if you do that, unless you turn animations off.

Core Graphics Drawing, Bad Performance Degredation

I am using the following core graphics code to draw a waveform of audio data as I am recording. I apologize for tons of code, but here it is (I found it here):
//
// WaveformView.m
//
// Created by Edward Majcher on 7/17/14.
//
#import "WaveformView.h"
//Gain applied to incoming samples
static CGFloat kGain = 10.;
//Number of samples displayed
static int kMaxWaveforms = 80.;
#interface WaveformView ()
#property (nonatomic) BOOL addToBuffer;
//Holds kMaxWaveforms number of incoming samples,
//80 is based on half the width of iPhone, adding a 1 pixel line between samples
#property (strong, nonatomic) NSMutableArray* bufferArray;
+ (float)RMS:(float *)buffer length:(int)bufferSize;
#end
#implementation WaveformView
- (void)awakeFromNib
{
[super awakeFromNib];
self.bufferArray = [NSMutableArray array];
}
-(void)updateBuffer:(float *)buffer withBufferSize:(UInt32)bufferSize
{
if (!self.addToBuffer) {
self.addToBuffer = YES;
return;
} else {
self.addToBuffer = NO;
}
float rms = [WaveformView RMS:buffer length:bufferSize];
if ([self.bufferArray count] == kMaxWaveforms) {
//##################################################
// [self.bufferArray removeObjectAtIndex:0];
}
[self.bufferArray addObject:#(rms * kGain)];
[self setNeedsDisplay];
}
+ (float)RMS:(float *)buffer length:(int)bufferSize {
float sum = 0.0;
for(int i = 0; i < bufferSize; i++) {
sum += buffer[i] * buffer[i];
}
return sqrtf( sum / bufferSize );
}
// *****************************************************
- (void)drawRect:(CGRect)rect
{
CGFloat midX = CGRectGetMidX(rect);
CGFloat maxX = CGRectGetMaxX(rect);
CGFloat midY = CGRectGetMidY(rect);
CGContextRef context = UIGraphicsGetCurrentContext();
// Draw out center line
CGContextSetStrokeColorWithColor(context, [UIColor whiteColor].CGColor);
CGContextSetLineWidth(context, 1.);
CGContextMoveToPoint(context, 0., midY);
CGContextAddLineToPoint(context, maxX, midY);
CGContextStrokePath(context);
CGFloat x = 0.;
for (NSNumber* n in self.bufferArray) {
CGFloat height = 20 * [n floatValue];
CGContextMoveToPoint(context, x, midY - height);
CGContextAddLineToPoint(context, x, midY + height);
CGContextStrokePath(context);
x += 2;
}
if ([self.bufferArray count] >= kMaxWaveforms) {
[self addMarkerInContext:context forX:midX forRect:rect];
} else {
[self addMarkerInContext:context forX:x forRect:rect];
}
}
- (void)addMarkerInContext:(CGContextRef)context forX:(CGFloat)x forRect:(CGRect)rect
{
CGFloat maxY = CGRectGetMaxY(rect);
CGContextSetStrokeColorWithColor(context, [UIColor greenColor].CGColor);
CGContextSetFillColorWithColor(context, [UIColor greenColor].CGColor);
CGContextFillEllipseInRect(context, CGRectMake(x - 1.5, 0, 3, 3));
CGContextMoveToPoint(context, x, 0 + 3);
CGContextAddLineToPoint(context, x, maxY - 3);
CGContextStrokePath(context);
CGContextFillEllipseInRect(context, CGRectMake(x - 1.5, maxY - 3, 3, 3));
}
#end
So as I am recording audio, the waveform drawing gets more and more jittery, kind of like a game that has bad frame rates. I tried contacting the owner of this piece of code, but no luck. I have never used core graphics, so I'm trying to figure out why performance is so bad. Performance starts to degrade at around 2-3 seconds worth of audio (the waveform doesn't even fill the screen).
My first question is, is this redrawing the entire audio history every time drawRect is called? If you look in the drawRect function (marked by asterisks), there is a variable called CGRect x. This seems to affect the position at which the waveform is being drawn (if you set it to 60 instead of 0, it starts at x=60 pixels instead of x=0 pixels).
From my viewController, I pass in the audioData which gets stored in the self.bufferArray property. So when that loop goes through to draw the data, it seems like it's starting at zero and working its way up every time drawRect is getting called, which means that for every new piece of audio data added, drawRect gets called, and it redraws the entire waveform plus the new piece of audio data.
If that is the problem, does anyone know how I can optimize this piece of code? I tried emptying the bufferArray after the loop so that it contained only new data, but that didn't work.
If this is not the problem, are there any core graphics experts that can figure out what the problem is?
I should also mention that I commented out a piece of code (marked with ### at signs) because I need the entire waveform. I don't want it to remove pieces of the waveform at the beginning. The iOS Voice Memos app can hold a waveform of audio without performance degradation.

text labels drawn in CGRect form an elliptical arc when images drawn using the same coordinates form a circular arc

I am new to Core Graphics and trying to understand why text labels I draw in CGRect form an elliptical arc when images I draw using the same coordinates form a circular arc.
The original code by Arthur Knopper creates circles wherever the screen is touched. By removing the touches method, I have been able to generate a series of small circles (dots) along a circular arc (uber circle). Each dot is centred on the perimeter of the uber circle (as shown below).
In order to label each dot I use the same point coordinates I used for placing the dot. However text labels form an elliptical arc even though dots form a circular arc (as shown below). Labels are also hidden by the dots when dots are filled. The reason for this is a complete mystery.
As a novice I am probably missing something basic in Core Graphics. If anyone could explain what that is and what I need to do to make both arcs circular and place labels on top of the dots I’d be most grateful.
Thanks.
Here is the code.
circleView.h
NSMutableArray *totalCircles;
int dotCount, limit;
float uberX, uberY, uberRadius, uberAngle, labelX,
labelY,dotRadius, dotsFilled, sectors, x, y;
CGPoint dotPosition;
CGRect boxBoundary;
CGContextRef context;
}
- (void)demo;
#end
And ...
-#implementation iOSCircleView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code
totalCircles = [[NSMutableArray alloc] init];
// Set background color
self.backgroundColor = [UIColor whiteColor];
}
return self;
} // frame a view for drawing
- (void)drawRect:(CGRect)rect {
[self demo];
}
- (void)demo {
context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
uberX = 120;
uberY = 160;
uberRadius = 30;
sectors = 16;
uberAngle = (2.0 * PI) / sectors;
dotRadius = 20;
dotsFilled = FALSE;
for (dotCount = 1; dotCount <= sectors; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // make new point for each dot
dotPosition = CGPointMake(x,y); // create each dot
NSLog(#"Circle%i: %#", dotCount, NSStringFromCGPoint(dotPosition));
[self autoLabel]; // text hides behind the dots
newCircle.circleCentre = dotPosition; // place each dot on the frame
[totalCircles addObject:newCircle];
[self setNeedsDisplay];
}
CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
dotCount = 1;
for (iOSCircle *circle in totalCircles) {
CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES); // draw the circles
NSLog(#"Dot %i Filled %i ", dotCount, dotsFilled);
switch (dotsFilled) {
case 1:
CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
//CGContextDrawPath(context, kCGPathFillStroke);
break;
default:
//CGContextStrokePath(context); // draw dot outline
break;
}
dotCount++;
}
} // draw circular dots in circular patterns
- (void)setSectorDotCoordinates {
x = uberX + (uberRadius * cos(uberAngle *dotCount) * 2);
y = uberY + (uberRadius * sin(uberAngle *dotCount) * 2);
} // calculate dot coordinates along a circular arc
- (void)autoLabel {
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, x+dotRadius, y+dotRadius);
[[NSString stringWithFormat:#"%i",dotCount] drawInRect:boxBoundary withFont:[UIFont systemFontOfSize:24] lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter];
}
Change the boxBoundary in autoLabel, CGRectMake creates a rectangle with one point coordinates and width and height, not two points:
(void)autoLabel {
CGContextSetStrokeColorWithColor(context, [[UIColor blackColor] CGColor]);
boxBoundary = CGRectMake(x-dotRadius, y-dotRadius, dotRadius*2, dotRadius*2);
[[NSString stringWithFormat:#"%i",dotCount] drawInRect:boxBoundary
withFont:[UIFont systemFontOfSize:24]
lineBreakMode:NSLineBreakByCharWrapping alignment:NSTextAlignmentCenter];
}
In your code the "boxes" containing the texts where bigger and bigger when you where going to the right. (the width and height were not fixed)
My updated code show labels that match the drawing order but text is still hidden when dots are filled.
I suspect I need to construct a path to write text in front of the dots and it’s already apparent that something like CGPathMoveToPoint is needed to start drawing from the 12 O'clock position.
Here’s the updated code. The first part draws and renders the dots
context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 0.5);
uberX = 160;
uberY = 240;
uberRadius = 52;
sectors = 16;
uberAngle = ((2.0 * PI) / sectors);
NSLog(#"%f %f %f %f", uberX, uberY, uberRadius, uberAngle);
dotRadius = 20;
dotsFilled = FALSE;
textOffset = 4; // add to y to centre the label
for (dotCount = 1; dotCount <= 4 /*sectors*/; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // create a new point for each dot
dotPosition = CGPointMake(x,y); // create each dot
NSLog(#"Circle%i: %#", dotCount, NSStringFromCGPoint(dotPosition));
newCircle.circleCentre = dotPosition; // place each dot on the frame
[totalCircles addObject:newCircle];
[self setNeedsDisplay];
}
CGContextSetShadowWithColor(context, CGSizeMake(-3 , 2), 4.0, [UIColor clearColor].CGColor);
dotCount = 1;
for (iOSCircle *circle in totalCircles) {
CGContextAddArc(context, circle.circleCentre.x, circle.circleCentre.y, circle.circleRadius, 0.0, M_PI * 2.0, YES);
// draw the circles
NSLog(#"Dot %i Filled %i ", dotCount, dotsFilled);
switch (dotsFilled) {
case 1:
CGContextSetFillColorWithColor(context, [[UIColor cyanColor] CGColor]);
CGContextDrawPath(context, kCGPathFillStroke);
break;
default:
CGContextStrokePath(context); // draw dot outline
break;
}
[self setSectorDotCoordinates]; // find point coordinates for each dot
dotCount++;
}
The code that draws the labels follow immediately afterwards.
// draw labels
for (dotCount = 1; dotCount <= sectors; dotCount++)
{
// Create a new iOSCircle Object
iOSCircle *newCircle = [[iOSCircle alloc] init];
newCircle.circleRadius = dotRadius;
[self setSectorDotCoordinates]; // find point coordinates for each dot
dotPosition = CGPointMake(x,y); // use point coordinates for label
[self autoLabel]; // THIS SHOWS TEXT BEHIND THE DOTS
}

Lined UITextView in IOS7

I have a subclass of a UITextView (Custom Control DALinedTextView) where i draw lined text. It works perfect on iOS5 and iOS6 but on iOS7 it fails (text does not match lines).
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 1.0f);
if (self.horizontalLineColor)
{
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, self.horizontalLineColor.CGColor);
// Create un-mutated floats outside of the for loop.
// Reduces memory access.
CGFloat baseOffset = 7.0f + self.font.descender;
CGFloat screenScale = [UIScreen mainScreen].scale;
CGFloat boundsX = self.bounds.origin.x;
CGFloat boundsWidth = self.bounds.size.width;
// Only draw lines that are visible on the screen.
// (As opposed to throughout the entire view's contents)
NSInteger firstVisibleLine = MAX(1, (self.contentOffset.y / self.font.lineHeight));
NSInteger lastVisibleLine = ceilf((self.contentOffset.y + self.bounds.size.height) / self.font.lineHeight);
for (NSInteger line = firstVisibleLine; line <= lastVisibleLine; ++line)
{
CGFloat linePointY = (baseOffset + (self.font.lineHeight * line));
// Rounding the point to the nearest pixel.
// Greatly reduces drawing time.
CGFloat roundedLinePointY = roundf(linePointY * screenScale) / screenScale;
CGContextMoveToPoint(context, boundsX, roundedLinePointY);
CGContextAddLineToPoint(context, boundsWidth, roundedLinePointY);
}
CGContextClosePath(context);
CGContextStrokePath(context);
}
if (self.verticalLineColor)
{
CGContextBeginPath(context);
CGContextSetStrokeColorWithColor(context, self.verticalLineColor.CGColor);
CGContextMoveToPoint(context, -1.0f, self.contentOffset.y);
CGContextAddLineToPoint(context, -1.0f, self.contentOffset.y + self.bounds.size.height);
CGContextClosePath(context);
CGContextStrokePath(context);
}
}
I know it's someting related to UIFont metrics..perphaps someone can help me out? I've change contentSize to intrinsicContentSize but it dos not work.
If i use systemFontOfSize it works perfectly, but with fontWithName it fails.
I've been struggling with this weird issue for quite a while, but finally stumbled upon a legit solution:
textView.layoutManager.usesFontLeading = NO;
This makes UITextView render text (almost) identically to UILabel.
Yes , i think you are using Custom Control DALinedTextView.
I am also facing a problem like you said in iOS7.
Even you can't scroll that control correctly in iOS7.
First i used that control and now i leaved it and using built-in UITextView. :D
Perhaps , you need to change the font size and text margin or text padding of your textView.
check this link.
It uses NSLayoutManagerDelegate, that is used in ios7.
For iOS 7, the styleString approach no longer works.
You have to use NSLayoutManagerDelegate, it is easy to use.
txtViewNote.layoutManager.delegate = self;
txtViewNote.backgroundColor = [UIColor colorWithPatternImage:[UIImage imageNamed:#"textView_bg_lines"]];
- (CGFloat)layoutManager:(NSLayoutManager *)layoutManager lineSpacingAfterGlyphAtIndex:(NSUInteger)glyphIndex withProposedLineFragmentRect:(CGRect)rect
{
return 20.5; // For really wide spacing
}

Resources