I have a UITextView where the user can create notes and save into a plist file.
I want to be able to show lines just like a normal notebook. The problem I have is
that the text won't align properly.
The image below explains the problem quite well.
This is the background I use to create the lines like the Notes.app
This is my code for creating the background for my UITextView:
textView.font = [UIFont fontWithName:#"MarkerFelt-Thin" size:19.0];
textView.backgroundColor = [UIColor colorWithPatternImage: [UIImage imageNamed: #"Notes.png"]];
I know that the UIFont.lineHeight property is only available in > iOS 4.x.
So I wonder if there is another solution to my problem?
You should try and draw your lines programmatically rather than using an image. Here's some sample code of how you could accomplish that. You can subclass UITextView and override it's drawRect: method.
NoteView.h
#import <UIKit/UIKit.h>
#interface NoteView : UITextView <UITextViewDelegate> {
}
#end
NoteView.m
#import "NoteView.h"
#implementation NoteView
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor = [UIColor colorWithRed:1.0f green:1.0f blue:0.6f alpha:1.0f];
self.font = [UIFont fontWithName:#"MarkerFelt-Thin" size:19];
}
return self;
}
- (void)drawRect:(CGRect)rect {
//Get the current drawing context
CGContextRef context = UIGraphicsGetCurrentContext();
//Set the line color and width
CGContextSetStrokeColorWithColor(context, [UIColor colorWithRed:0.0f green:0.0f blue:0.0f alpha:0.2f].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 = 0; x < numberOfLines; x++) {
//0.5f offset lines up line with pixel boundary
CGContextMoveToPoint(context, self.bounds.origin.x, self.font.leading*x + 0.5f + baselineOffset);
CGContextAddLineToPoint(context, self.bounds.size.width, self.font.leading*x + 0.5f + baselineOffset);
}
//Close our Path and Stroke (draw) it
CGContextClosePath(context);
CGContextStrokePath(context);
}
#end
MyViewController.h
#import <UIKit/UIKit.h>
#import "NoteView.h"
#interface MyViewController : UIViewController <UITextViewDelegate> {
NoteView *note;
}
#property (nonatomic, retain) NoteView *note;
#end
MyViewController.m
#import "MyViewController.h"
#import "NoteView.h"
#define KEYBOARD_HEIGHT 216
#implementation MyViewController
#synthesize note;
- (void)loadView {
[super loadView];
self.note = [[[NoteView alloc] initWithFrame:self.view.bounds] autorelease];
[self.view addSubview:note];
note.delegate = self;
note.text = #"This is the first line.\nThis is the second line.\nThis is the ... line.\nThis is the ... line.\nThis is the ... line.\nThis is the ... line.\nThis is the ... line.\n";
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
[note setNeedsDisplay];
}
- (void)textViewDidBeginEditing:(UITextView *)textView {
CGRect frame = self.view.bounds;
frame.size.height -= KEYBOARD_HEIGHT;
note.frame = frame;
}
- (void)textViewDidEndEditing:(UITextView *)textView {
note.frame = self.view.bounds;
}
- (void)dealloc {
[note release];
[super dealloc];
}
Take a look at Apple's documentation for Managing the Keyboard, specifically "Moving Content That Is Located Under the Keyboard". It explains how to listen for NSNotifcations and adjust your views properly.
I think the problem is with your image, the yellow space over the line is creating the problem.
You should edit the image.
And nice work.
Related
I have a UICollectionView where some of the cells should have a dashed border and some of them should have a solid border. Also, the cells can be of varying size depending on the content that is present in the data model.
The problem I am having is that I cannot get the dashed border to be the same size as the collection view cell and again, the cell size can change based on the content. But basically, the cell should either have a dashed border or a solid border. The solid border is easy to get to resize to the correct size.
Here is a picture of what it looks like right now. The dashed border is colored green just to make it easier to see.
Here is the view hierarchy debug view. There are two dashed borders here because I have been experimenting. The green border is a sublayer on the UICollectionViewCell's root layer. The grey border is a sublayer of a separate view that is a subview of the collection view cell's contentView property.
Code
Approach 1 - add a dedicated view with a sublayer
Here I am trying to add a UIView subclass that has a dashed border. Then, when I need to show the dashed border or hide the dashed border, I just set the hidden property of the view accordingly. This works fine, except I cannot get the dashed border sublayer to resize.
The view is resizing to be the correct width and height based on the AutoLayout constraints, as can be seen in the view hierarchy debugger screenshot above. But the sublayer is still the original size (approximately 50px x 50px, which I guess is coming from the UICollectionView because I am not specifying that size anywhere).
For this implementation, I have a custom UIView subclass called MyResizableSublayerView. It overrides layoutSublayersOfLayer to handle the resizing of the sublayer, or at least that is what is supposed to be happening, but clearly it is not working.
But then the MyResizableSublayerView class is used in the collection view cell to add the dashed border to the view hierarchy.
MyResizableSublayerView
#interface MyResizableSublayerView : UIView
#property (strong, nonatomic) CAShapeLayer *borderLayer;
+ (instancetype)viewWithBorderSublayer:(CAShapeLayer *)shapeLayer;
#end
#implementation MyResizableSublayerView
+ (instancetype)viewWithBorderSublayer:(CAShapeLayer *)shapeLayer {
CIResizableSublayerView *view = [[MyResizableSublayerView alloc] init];
view.borderLayer = shapeLayer;
return view;
}
- (void)setBorderLayer:(CAShapeLayer *)borderLayer {
if (self->_borderLayer) {
[self->_borderLayer removeFromSuperlayer];
}
self->_borderLayer = borderLayer;
[self.layer addSublayer:self->_borderLayer];
}
- (void)layoutSublayersOfLayer:(CALayer *)layer {
[super layoutSublayersOfLayer:layer];
self.borderLayer.frame = layer.bounds;
}
#end
MyCollectionViewCell
#interface MyCollectionViewCell ()
#property (strong, nonatomic) MyResizableSublayerView *unavailableBorderView;
#end
#implementation MyCollectionViewCell
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self initialize];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initialize];
}
return self;
}
- (void)initialize {
self.contentView.layer.cornerRadius = 4.0f;
self.contentView.layer.borderWidth = 1.0f;
//... add other subviews
[self.contentView addSubview:self.unavailableBorderView];
[NSLayoutConstraint activateConstraints:#[
[self.contentView.widthAnchor constraintLessThanOrEqualToConstant:250.0],
[self.contentView.widthAnchor constraintGreaterThanOrEqualToConstant:100.0],
[self.unavailableBorderView.leadingAnchor constraintEqualToAnchor:self.contentView.leadingAnchor],
[self.unavailableBorderView.topAnchor constraintEqualToAnchor:self.contentView.topAnchor],
[self.unavailableBorderView.trailingAnchor constraintEqualToAnchor:self.contentView.trailingAnchor],
[self.unavailableBorderView.bottomAnchor constraintEqualToAnchor:self.contentView.bottomAnchor],
//... constraints for other views
]];
}
- (MyResizableSublayerView *)unavailableBorderView {
if (!self->_unavailableBorderView) {
CAShapeLayer *layer = [CAShapeLayer layer];
layer.strokeColor = [UIColor colorWithRed:0xE0/255.0 green:0xE0/255.0 blue:0xE0/255.0 alpha:1.0].CGColor;
layer.lineWidth = 4.0;
layer.lineJoin = kCALineJoinRound;
layer.fillColor = [UIColor clearColor].CGColor;
layer.lineDashPattern = #[#4, #4];
layer.frame = self.contentView.bounds;
layer.path = [UIBezierPath bezierPathWithRoundedRect:self.contentView.bounds cornerRadius:self.contentView.layer.cornerRadius].CGPath;
self->_unavailableBorderView = [MyResizableSublayerView viewWithBorderSublayer:layer];
self->_unavailableBorderView.translatesAutoresizingMaskIntoConstraints = NO;
self->_unavailableBorderView.layer.cornerRadius = self.contentView.layer.cornerRadius;
self->_unavailableBorderView.backgroundColor = [UIColor colorWithWhite:1.0 alpha:0.0];
}
return self->_unavailableBorderView;
}
//... more logic
#end
Approach 2 - add directly to the UICollectionViewCell
For this approach, I add the CAShapeLayer directly to the UICollectionViewCell and then override the layoutSublayersOfLayer to try to resize the dashed border sublayer, but this is not working either.
#interface MyCollectionViewCell ()
#property (strong, nonatomic) CAShapeLayer *unavailableBorderLayer;
#end
#implementation MyCollectionViewCell
- (instancetype)init {
return [self initWithFrame:CGRectZero];
}
- (instancetype)initWithCoder:(NSCoder *)coder {
if (self = [super initWithCoder:coder]) {
[self initialize];
}
return self;
}
- (instancetype)initWithFrame:(CGRect)frame {
if (self = [super initWithFrame:frame]) {
[self initialize];
}
return self;
}
- (void)initialize {
self.contentView.layer.cornerRadius = 4.0f;
self.contentView.layer.borderWidth = 1.0f;
//... add other subviews
[NSLayoutConstraint activateConstraints:#[
[self.contentView.widthAnchor constraintLessThanOrEqualToConstant:250.0],
[self.contentView.widthAnchor constraintGreaterThanOrEqualToConstant:100.0],
//... constraints for other views
]];
CAShapeLayer *layer = [CAShapeLayer layer];
layer.strokeColor = [UIColor greenColor].CGColor;
layer.lineWidth = 2.0;
layer.lineJoin = kCALineJoinRound;
layer.fillColor = [UIColor clearColor].CGColor;
layer.lineDashPattern = #[#4, #4];
layer.frame = self.contentView.bounds;
layer.path = [UIBezierPath bezierPathWithRoundedRect:self.contentView.bounds cornerRadius:self.contentView.layer.cornerRadius].CGPath;
self->_unavailableBorderLayer = layer;
[self.layer addSublayer:self->_unavailableBorderLayer];
}
- (void)layoutSublayersOfLayer:(CALayer *)layer {
[super layoutSublayersOfLayer:layer];
self.unavailableBorderLayer.frame = self.bounds;
}
//... more logic
#end
Questions
I have a couple of questions about this.
What is wrong with my code that is not allowing the dashed border to resize to be the same size as the collection view cell?
Which approach is the best approach to add a dashed border to the collection view cell. Or is there a better approach than the ones that I have listed here? Again my goal is to be able to show or hide the dashed border and for it to be the same size as the collection view cell, which is dynamically sized.
It's not quite clear what you're doing with constraints on the content view ... however, if you are getting the layout you want, except for the dashed borders, give this a try.
First, instead of layoutSublayersOfLayer, use:
- (void)layoutSubviews {
[super layoutSubviews];
_unavailableBorderLayer.frame = self.bounds;
}
Im trying to setMinimumTrackImage of the slider using an image with CAGradientLayer, lets say using blue and red colors.
what happens is that the full track gets the gradient color, its starts with red, and sliding the Thumb to the right reveals the blue.
I want the color to start from red to blue up to the Thumb, ands "stretch" as the Thumb moves.
any ideas ? I though about setting the slider.maximumValue = slider...width
and change the gradient image as I listen to the slider value change but it didn't work
I don't think you'll be successful trying to set the min track image.
Options are a completely custom slider...
or
Set the min track image to a clear image, add an imageView with the gradient image behind the slider, stretch the frame of the imageView to match the thumb movement.
Here's an example:
and the code (just a starting point... would be much better to wrap it into a subclass):
SliderTestViewController.h
//
// SliderTestViewController.h
//
// Created by Don Mag on 10/31/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface SliderTestViewController : UIViewController
#end
NS_ASSUME_NONNULL_END
SliderTestViewController.m
//
// SliderTestViewController.m
//
// Created by Don Mag on 10/31/19.
//
#import "SliderTestViewController.h"
#import "UIImage+Utils.h"
#interface SliderTestViewController ()
#property (strong, nonatomic) UIImageView *theFakeSliderTrackImageView;
#property (strong, nonatomic) UISlider *theSlider;
#property (strong, nonatomic) NSLayoutConstraint *imgWidthConstraint;
#end
#implementation SliderTestViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.view.backgroundColor = [UIColor whiteColor];
// instantiate a slider
_theSlider = [UISlider new];
// instantiate an image view to use as our custom / fake "left side" of the slider track
_theFakeSliderTrackImageView = [UIImageView new];
// we want the image to stretch
_theFakeSliderTrackImageView.contentMode = UIViewContentModeScaleToFill;
// create a horizontal gradient image to use for our "left side" of the slider track
// the image will be stretched... using a width of 128 seems reasonable
UIImage *gradImg = [UIImage gradientImageWithSize:CGSizeMake(128.0, 4.0) startColor:[UIColor blueColor] endColor:[UIColor redColor] startPoint:CGPointMake(0.0, 0.0) endPoint:CGPointMake(1.0, 0.0)];
// set the gradient image to our image view
_theFakeSliderTrackImageView.image = gradImg;
// create a clear image to use for the slider's min track image
UIImage *clearImg = [UIImage imageWithColor:[UIColor clearColor] size:CGSizeMake(1.0, 1.0)];
// set min track image to clear image
[_theSlider setMinimumTrackImage:clearImg forState:UIControlStateNormal];
// set max track image if desired
// [_theSlider setMaximumTrackImage:anImage forState:UIControlStateNormal];
_theFakeSliderTrackImageView.translatesAutoresizingMaskIntoConstraints = NO;
_theSlider.translatesAutoresizingMaskIntoConstraints = NO;
[self.view addSubview:_theFakeSliderTrackImageView];
[self.view addSubview:_theSlider];
[NSLayoutConstraint activateConstraints:#[
// constrain the slider centerY with 20-pts leading / trailing
[_theSlider.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor],
[_theSlider.leadingAnchor constraintEqualToAnchor:self.view.leadingAnchor constant:20.0],
[_theSlider.trailingAnchor constraintEqualToAnchor:self.view.trailingAnchor constant:-20.0],
// constrain image view centerY to slider centerY
[_theFakeSliderTrackImageView.centerYAnchor constraintEqualToAnchor:_theSlider.centerYAnchor constant:0.0],
// constrain image view leading to slider leading
[_theFakeSliderTrackImageView.leadingAnchor constraintEqualToAnchor:_theSlider.leadingAnchor constant:0.0],
// image view height to 5-pts (adjust as desired)
[_theFakeSliderTrackImageView.heightAnchor constraintEqualToConstant:5.0],
]];
// init imageView width constraint to 0.0
_imgWidthConstraint = [_theFakeSliderTrackImageView.widthAnchor constraintEqualToConstant:0.0];
_imgWidthConstraint.active = YES;
[_theSlider addTarget:self action:#selector(sliderChanged:) forControlEvents:UIControlEventValueChanged];
}
- (void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self updateSliderGradientImage];
}
- (void)updateSliderGradientImage {
// set "fake track" imageView width to origin.x of thumb rect (plus 2 for good measure)
CGRect trackRect = [_theSlider trackRectForBounds:_theSlider.bounds];
CGRect thumbRect = [_theSlider thumbRectForBounds:_theSlider.bounds trackRect:trackRect value:_theSlider.value];
_imgWidthConstraint.constant = thumbRect.origin.x + 2;
}
- (void)sliderChanged:(id)sender {
[self updateSliderGradientImage];
}
#end
UIImage+Utils.h
//
// UIImage+Utils.h
//
// Created by Don Mag on 10/31/19.
//
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface UIImage (Utils)
+ (nullable UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size;
+ (nullable UIImage *)gradientImageWithSize:(CGSize)size startColor:(UIColor *)startColor endColor:(UIColor *)endColor startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint;
#end
NS_ASSUME_NONNULL_END
UIImage+Utils.m
//
// UIImage+Utils.m
//
// Created by Don Mag on 10/31/19.
//
#import "UIImage+Utils.h"
#implementation UIImage (Utils)
+ (UIImage *)imageWithColor:(UIColor *)color size:(CGSize)size {
if (!color || size.height < 1 || size.width < 1)
return nil;
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
UIImage *image = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull context) {
[color setFill];
[context fillRect:renderer.format.bounds];
}];
return image;
}
+ (UIImage *)gradientImageWithSize:(CGSize)size startColor:(UIColor *)startColor endColor:(UIColor *)endColor startPoint:(CGPoint)startPoint endPoint:(CGPoint)endPoint {
if (!startColor || !endColor)
return nil;
CFArrayRef colors = (__bridge CFArrayRef) [NSArray arrayWithObjects:
(id)startColor.CGColor,
(id)endColor.CGColor,
nil];
CGGradientRef g = CGGradientCreateWithColors(nil, colors, nil);
startPoint.x *= size.width;
startPoint.y *= size.height;
endPoint.x *= size.width;
endPoint.y *= size.height;
UIGraphicsImageRenderer *renderer = [[UIGraphicsImageRenderer alloc] initWithSize:size];
UIImage *gradientImage = [renderer imageWithActions:^(UIGraphicsImageRendererContext * _Nonnull rendererContext) {
CGContextDrawLinearGradient(rendererContext.CGContext, g, startPoint, endPoint, kCGGradientDrawsAfterEndLocation);
}];
return gradientImage;
}
#end
In my TestApp, I created a class "Carousel" which should let me create a swipe menu with a UIPageControl in an easy way (by simply creating an instance of the class Carousel).
Carousel is a subclass of UIView
At init, it creates an UIView, containing UIScrollView, UIPageControl
I can add further UIViews to the scroll view
I don't know if this is the proper way to do it, but my example worked quite well in my TestApp. Swiping between pages works perfectly and the display of the current page in the UIPageControl is correct.
If there were not one single problem: The UIPageControl sometimes reacts to clicks/taps (I only tested in Simulator so far!), sometimes it doesn't. Let's say most of the time it doesn't. I couldn't find out yet when it does, for me it's just random...
As you can see below, I added
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
to my code. I thought this would do the proper handling of taps? But unfortunately, pageChange doesn't get always called (so the value of the UIPageControl doesn't change every time I click).
I would appreciate any input on this because I couldn't find any solution on this yet.
This is what I have so far:
Carousel.h
#import <UIKit/UIKit.h>
#interface Carousel : UIView {
UIScrollView *scrollView;
UIPageControl *pageControl;
BOOL pageControlBeingUsed;
}
- (void)addView:(UIView *)view;
- (void)setTotalPages:(NSUInteger)pages;
- (void)setCurrentPage:(NSUInteger)current;
- (void)createPageControlAt:(CGRect)cg;
- (void)createScrollViewAt:(CGRect)cg;
#end
Carousel.m
#import "Carousel.h"
#implementation Carousel
- (id)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Create a scroll view
scrollView = [[UIScrollView alloc] init];
[self addSubview:scrollView];
scrollView.delegate = (id) self;
// Init Page Control
pageControl = [[UIPageControl alloc] init];
[pageControl addTarget:self action:#selector(pageChange:) forControlEvents:UIControlEventValueChanged];
[self addSubview:pageControl];
}
return self;
}
- (IBAction)pageChange:(id)sender {
CGRect frame = scrollView.frame;
frame.origin.x = frame.size.width * pageControl.currentPage;
frame.origin.y = 0;
[scrollView scrollRectToVisible:frame animated:TRUE];
NSLog(#"%i", pageControl.currentPage);
}
- (void)addView:(UIView *)view {
[scrollView addSubview:view];
}
- (void)createPageControlAt:(CGRect)cg {
pageControl.frame = cg;
}
- (void)setTotalPages:(NSUInteger)pages {
pageControl.numberOfPages = pages;
}
- (void)setCurrentPage:(NSUInteger)current {
pageControl.currentPage = current;
}
- (void)createScrollViewAt:(CGRect)cg {
[scrollView setPagingEnabled:TRUE];
scrollView.frame = cg;
scrollView.contentSize = CGSizeMake(scrollView.frame.size.width*pageControl.numberOfPages, scrollView.frame.size.height);
[scrollView setShowsHorizontalScrollIndicator:FALSE];
[scrollView setShowsVerticalScrollIndicator:FALSE];
}
- (void)scrollViewDidScroll:(UIScrollView *)scrollView {
float frac = scrollView.contentOffset.x / scrollView.frame.size.width;
NSUInteger page = lround(frac);
pageControl.currentPage = page;
}
#end
ViewController.m (somewhere in viewDidLoad)
Carousel *carousel = [[Carousel alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];
for (int i=0; i<5; i++) {
UIView *view = [[UIView alloc] initWithFrame:CGRectMake(i * 320, 0, 320, 420)];
UIColor *color;
if(i%3==0) color = [UIColor blueColor];
else if(i%3==1) color = [UIColor redColor];
else color = [UIColor purpleColor];
view.backgroundColor = color;
[carousel addView:view];
view = nil;
}
[carousel setTotalPages:5];
[carousel setCurrentPage:0];
[carousel createPageControlAt:CGRectMake(0,420,320,40)];
[carousel createScrollViewAt:CGRectMake(0,0,320,420)];
Your code is correct. Most likely the frame of your pageControl is pretty small, so theres not a lot of area to look for touch events. You would need to increase the size of the height of pageControl in order to make sure taps are recognized all of the time.
I have a storyboard which loads loads a custom UIView. Also a sub view is added to the view in the storyboard. It worked fine until I overwrote the drawRect method of the sub view, then I just saw a black rectangle instead of the subview. Here is the code:
#import <UIKit/UIKit.h>
#import "MySubview.h"
#interface MyView : UIView
#end
#import "MyView.h"
#implementation MyView
- (void) awakeFromNib
{
CGRect frame = [self frame];
MySubview* sv = [[MySubview alloc] initWithFrame:frame];
[self addSubview:sv];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
#end
#import <UIKit/UIKit.h>
#interface MySubview : UIView
#property (retain, nonatomic) NSString* text;
#property (retain, nonatomic) UILabel* label;
#end
#import "MySubview.h"
#implementation MySubview
#synthesize text, label;
- (void)attachLabel
{
text = #"Hello";
label = [[UILabel alloc] init];
[label setText:text];
[label setFont:[UIFont fontWithName:#"Futura" size:18]];
[label setBackgroundColor:[UIColor clearColor]];
[label sizeToFit];
CGRect labelFrame = label.frame;
labelFrame.origin.x = (self.frame.size.width - labelFrame.size.width) / 2;
labelFrame.origin.y = (self.frame.size.height - labelFrame.size.height) / 2;
label.frame = labelFrame;
[self addSubview:label];
}
- (void)awakeFromNib
{
[self attachLabel];
}
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
[self attachLabel];
}
return self;
}
// Works if I comment this out!
- (void)drawRect:(CGRect)rect
{
}
#end
Update - Added drawing code below:
- (void)drawRectWithRoundBorders:(CGRect)rect
{
[super drawRect:rect];
// Parameters used for drawing.
const CGFloat lineWidth = 5;
const CGFloat shadowOffset = 3;
const CGFloat shadowBlur = 4;
const CGFloat spaceToBB = 10; // Space to the bounding box of this view.
const CGFloat cornerRadii = 5;
const CGFloat lineColor[4] = { 0, 0, 0, 1 };
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ctx, lineWidth);
CGContextSetStrokeColor(ctx, lineColor);
CGContextSetShadow(ctx, CGSizeMake(shadowOffset, shadowOffset), shadowBlur);
CGRect innerRect = rect;
innerRect.size.width -= 2*spaceToBB;
innerRect.size.height -= 2*spaceToBB;
innerRect.origin.x += spaceToBB;
innerRect.origin.y += spaceToBB;
UIBezierPath *path =
[UIBezierPath bezierPathWithRoundedRect:innerRect
byRoundingCorners:UIRectCornerAllCorners
cornerRadii:CGSizeMake(cornerRadii, cornerRadii)
];
CGContextAddPath(ctx, path.CGPath);
CGContextStrokePath(ctx);
}
- (void)drawRect:(CGRect)rect
{
[self drawRectWithRoundBorders:rect];
}
Update
It seems to work when I fill the bounding box of the sub view with some color first.
CGContextRef ctx = UIGraphicsGetCurrentContext();
CGFloat white[] = {1, 1, 1, 0.5};
CGContextSetFillColor(ctx, white);
CGContextAddRect(ctx, rect);
CGContextFillPath(ctx);
Just add [self setOpaque:NO] in your initWithFrame: method.
That's because your drawRect is doing nothing. You override drawRect if you want to do custom drawing. So:
If you don't want to do custom drawing then don't override drawRect.
If you do want to do custom drawing then actually do something in drawRect.
I have a nsscroll view in my application and i made a subclass of nsscrollview to add a nsgradient but it doesn't work this is my code in my implementation file:
#import "scrollview.h"
#implementation scrollview
#synthesize startingColor;
#synthesize endingColor;
#synthesize angle;
- (id)initWithFrame:(NSRect)frame {
self = [super initWithFrame:frame];
if (self) {
// Initialization code here.
[self setStartingColor:[NSColor colorWithCalibratedRed:0.941 green:0.941 blue:0.941 alpha:1]];
[self setEndingColor:[NSColor colorWithCalibratedRed:0.6588 green:0.6588 blue:0.6588 alpha:1]];
[self setAngle:90];
}
return self;
}
- (void)drawRect:(NSRect)rect {
NSBezierPath* roundRectPath = [NSBezierPath bezierPathWithRoundedRect: [self bounds] xRadius:10 yRadius:10];
[roundRectPath addClip];
if (endingColor == nil || [startingColor isEqual:endingColor]) {
// Fill view with a standard background color
[startingColor set];
NSRectFill(rect);
}
else {
// Fill view with a top-down gradient
// from startingColor to endingColor
NSGradient* aGradient = [[NSGradient alloc]
initWithStartingColor:startingColor
endingColor:endingColor];
[aGradient drawInRect:[self bounds] angle:angle];
}
}
The first step is to create a custom NSView subclass that draws a gradient:
GradientBackgroundView.h:
#interface GradientBackgroundView : NSView
{}
#end
GradientBackgroundView.m:
#import "GradientBackgroundView.h"
#implementation GradientBackgroundView
- (void) drawRect:(NSRect)dirtyRect
{
NSGradient *gradient = [[[NSGradient alloc] initWithStartingColor:[NSColor redColor] endingColor:[NSColor greenColor]] autorelease];
[gradient drawInRect:[self bounds] angle:90];
}
#end
The next step is to make the scroll view's document view an instance of this class (instead of plain NSView).
In IB, double-click your scroll view, and in the Identity pane set the Class to GradientBackgroundView.
From this point on, things are handled pretty much in the standard way. You can add subviews to the document view, resize it, etc. Here's a screenshot: