UIView drawRect EXC_BAD_ACCESS on Recreate a Custon UIView - uiview

I having some problems with drawRect and create a new instance of a UIView custom class.
My question is: How can i use of drawrect?
I created a UIViewController with a UIScrollView and inside this UIScrollView about 50 UIViews. Each UIView create inside 3 elements (Custom UIViews) each one with a drawrect:
- (void)drawRect:(CGRect)rect
{
CGContextRef c = UIGraphicsGetCurrentContext();
CGContextClearRect(c, self.bounds);
CGContextSetStrokeColorWithColor(c, color.CGColor);
CGContextSetLineCap(c, kCGLineCapRound);
CGContextSetInterpolationQuality(c, kCGInterpolationHigh);
CGContextSetLineWidth(c, thickness);
CGContextAddArc(c, self.frame.size.width/2, self.frame.size.height/2, radius, angleIni*M_PI/180, angleEnd*M_PI/180, 0);
CGContextStrokePath(c);
}
At first it works like a charm, but when i try to remove this UIViewController and Create it again I'm getting a EXC_BAD_ACCESS.
If I remove the drawrect code everything begin to work again with no EXC_BAD_ACCESS. So I conclued tha my problem ismy drawrect method.
Is there some right way to true remove my UIviewController? On dealloc I'm removing all my UIViews inside UIScrollView and setting it to nil, besides I'm setting UIScrollView to nil too, like:
for (UIView *item in MyUICscrollView.subviews)
{
[item removeFromSuperview];
item = nil;
}
MyUICscrollView = nil;
Had someone the same issue?

Related

How to draw on a UIView with core graphics?

I can't figure out why I can't draw on the UIView layer. Is that possible?
My example code i am using:
#implementation Menu
UIView *window;
UIWindow *mainWindow;
CGSize viewwidth;
viewwidth=[[UIScreen mainScreen] bounds].size;
// viewwidth.width or viewwidth.height
//My UIView
window = [[UIView alloc]initWithFrame:CGRectMake(0, 0, viewwidth.width, viewwidth.height)];
window.backgroundColor = [UIColor whiteColor];
window.layer.opacity = 1.0f;
[window setNeedsDisplay];
[mainWindow addSubview: window];
My method drawRect for drawing:
- (void)drawRect:(CGRect)rect
{
// Drawing code
CGRect rectangle = CGRectMake(0, 0, 320, 100);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.0); //this is the transparent color
CGContextSetRGBStrokeColor(context, 0.0, 0.0, 0.0, 0.5);
CGContextFillRect(context, rectangle);
CGContextStrokeRect(context, rectangle); //this will draw the border
}
Am I doing something wrong, the problem is i get a empty white screen?
When you create a project and chose to use Storyboards you will set a ViewController to your Main.storyboard in Interface Builder.
There are differences between iOS Apps that use SceneDelegates and those who do not (iOS before 13.0). But your chosen ViewController will be assigned to be used from within your Main Window/Scene when the App will be loaded. In AppDelegate.m (before iOS 13) or SceneDelegate.m (starting iOS 13) you could directly access or even assign this yourself to the self.window.rootViewController property.
Do to inheritance of UIViewController which come with a basic UIView ready to work with under self.view (in this example inside ViewController.m) you can place more Subviews to that property with [self.view addSubview:yourOtherView].
-(void)drawRect:(CGRect)rect is a method that belongs to UIView and will be called when the view is set to update or layout. But it is important to know that this is not the only method where you can place drawing code. UIView:drawRect can be avoided to lower the Energy impact and workload you app is consuming when you don't need repeatedly redrawing the graphics. Also the workload is heavy influenced to be lower when you are able to lower the CGRect size to draw inside.
One word to value naming.. you will have less confusing code when values are named after the content they belong to. So naming a property "window" which is actually a UIView will stress you when your project becomes more complex. So you created a local value with name mainWindow while window is already existing. When doing so you will have to assign your mainWindow to the apps UIWindow yourself to make it available. And the UIView you named window can confuse you while _window or self.window is already existing as part of the AppDelegate or SceneDelegate. And as mentioned above you have a UIView that belongs to the UIViewController you assigned in Storyboard or in code.
Your drawing code is valid. It may be invisible to you because of transparency you choose to go for.
below some code to compare to for your ViewController.
#import "ViewController.h"
#import "MyGraphicView.h"
#interface ViewController ()
#property (nonatomic) MyGraphicView *graphicView;
#end
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
_graphicView = [[MyGraphicView alloc] initWithFrame:self.view.frame];
[self.view addSubview:_graphicView];
}
#end
and a MyGraphicView.h
#import <UIKit/UIKit.h>
NS_ASSUME_NONNULL_BEGIN
#interface MyGraphicView : UIView
#end
NS_ASSUME_NONNULL_END
and corresponding MyGraphicView.h
#import "MyGraphicView.h"
#implementation MyGraphicView
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
// This method is called when a view is first displayed or when an event occurs that invalidates a visible part of the view.
- (void)drawRect:(CGRect)rect {
CGRect rectangle = CGRectMake(0, 0, 320, 100);
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetRGBFillColor(context, 1.0, 1.0, 1.0, 0.0);
CGContextSetRGBStrokeColor(context, 0.0, 1.0, 0.0, 0.5);
CGContextFillRect(context, rectangle);
CGContextStrokeRect(context, rectangle);
}
#end

Custom UIView Subclass's drawRect not being called inside UITableViewCell

Background:
I have a subclass of UITableViewCell whose layout is defined as a prototype cell in a storyboard. One of the cell's IBOutlets is a subclass of UIView I made called BorderedProfileImageView. This class's drawRect: function is overridden as follows:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetStrokeColorWithColor(context, [[UIColor blueColor] CGColor]);
CGContextSetLineWidth(context, 1.f);
CGFloat halfWidth = self.bounds.size.width/2;
NSInteger radius = sqrt(pow(halfWidth, 2) + pow(halfWidth, 2)) - 8.5;
CGContextAddArc(context,self.bounds.size.width/2,self.bounds.size.height/2,radius,0,2*3.1415926535898,1);
CGContextStrokePath(context);
}
This adds a blue ring to the UIView.
Problem:
The blue ring never appears, and BorderdProfileImageView's drawRect: function never gets called. The cell still draws, the UIImageView which is inside the BorderdProfileImageView gets drawn. I do call setNeedsDisplay on the BorderedProfileImageView when setting up the cell.
Why is drawRect: not getting called?
I figured it out.
The object in IB which was set to the BorderedProfileImageView was originally a UIImageView. Changing this object to a UIView and re-linking has resulted in the desired behavior.

IOS - Working with autolayout and drawRect

I'm working with autolayout and I have a RoundView class (subview of UIButton) which drawRect method is:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGFloat lineWidth = 0.0;
CGContextSetLineWidth(context, lineWidth);
CGContextSetFillColorWithColor(context, _currentBackgroundColor.CGColor);
CGContextAddEllipseInRect(context, self.bounds);
CGContextFillPath(context);
}
Then I add it to self (a UIView), set its width and height using autolayout. Everything looks great.
But when I change its size constraints (to have a bigger view), and call
[self layoutIfNeeded];
in an animation block, the circle pixels look bigger and ugly.
Do I do this the correct way ?
I just got it from this post !
The problem was that the os wasn't calling drawRect at each animation step.
To force it, in RoundView's init:
self.contentMode = UIViewContentModeRedraw;
You need to take the content scale into account for the retina displays [UIDevice mainScreen].scale. See answers to this (question)[CGContext and retina display

How Can I draw shapes on a UIImageView that's in my main view? getting invalid context error?

In my main view I have a user-defined class drawSquare that is a subclass of UIImageView. In drawSquare.m I have the normal drawRect method shown below:
- (void)drawRect:(CGRect)rect
{
CGContextRef context = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(context, 2.0);
CGContextSetStrokeColorWithColor(context,[UIColor redColor].CGColor);
redSquare = CGRectMake(2,2,30,34);
CGContextAddRect(context, redSquare);
CGContextStrokePath(context);
}
In my main view controller I have an object named word1Area of type drawSquare as an IBOutlet.
In my viewDidLoad method of my main view controller I have the lines:
[self.view addSubview:word1Area];
[word1Area drawRect:word1Area.frame];
I get an "invalid context" run time error. I have tried other variations of calling the drawRect method but keep getting the same error. Thanks for any help.
You cannot call drawRect yourself - you can tell the view setNeedsDisplay.

Custom Drawing with Xib file present

I have a view that I want to add some custom drawing to.
I know how to do this with a View that isn't connected to a Nib/Xib file
you write the drawing code in the -drawRect: method.
But if I init the view using
[[MyView alloc] initWithNibName:#"MyView" bundle:[NSBundle mainBundle]];
-drawRect: of course doesn't get called. I tried doing the below code in -viewDidLoad
CGRect rect = [[self view] bounds];
CGContextRef ref = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ref, 2.0);
CGContextSetRGBStrokeColor(ref, 1.0, 1.0, 1.0, 1.0);
CGContextSetRGBFillColor(ref, 0, 0, 0, 0);
CGContextAddRect(ref, CGRectMake(1, 1, rect.size.width - 10, rect.size.height - 10));
CGContextStrokePath(ref);
CGContextDrawPath(ref, kCGPathFillStroke);
But nothing get drawn. Any ideas?
I think the issue is that you're treating a view and its view controller as interchangeable. For example, there's no -[UIView initWithNibName:bundle:] method — that's a UIViewController method.
Furthermore, a view isn't really like a "canvas" for drawing. A view will be asked to draw itself; in general, it won't be drawn into from "outside."
So:
Rename your subclass of UIViewController from MyView to MyViewController.
Create a new UIView subclass named MyView.
Add a -drawRect: method to your new MyView class that does the drawing you want.
Finally, set the Custom Class of your view controller's view in Interface Builder to MyView using the Identity Inspector.
For example, you should be able to use this for your -[MyView drawRect:] implementation:
- (void)drawRect:(CGRect)rect {
CGRect bounds = [[self view] bounds];
CGContextRef ref = UIGraphicsGetCurrentContext();
CGContextSetLineWidth(ref, 2.0);
CGContextSetRGBStrokeColor(ref, 1.0, 1.0, 1.0, 1.0);
CGContextSetRGBFillColor(ref, 0, 0, 0, 0);
CGContextAddRect(ref, CGRectMake(1, 1, bounds.size.width - 10, bounds.size.height - 10));
CGContextStrokePath(ref);
CGContextDrawPath(ref, kCGPathFillStroke);
}
The drawing will be clipped to the update rectangle passed in.
Here are three possible solutions, depending on your constraints.
Set the class of the NSView object in the Xib file to be one of your custom classes. You mention that you know how to do custom drawing with a "hard-coded" NSView subclass, and this would work the same way. When the Xib file is loaded, the NSView is loaded into memory as an object of your custom class instead of the default. Changing the class type of the view in the Xib is simple with Interface Builder.
If the NSView object in the Xib file is already some kind of custom view that you do not want to override, you can add a subview to that view in Interface Builder, and simply make the subview into a custom class of your design (as in #1)
You can programmatically add a subview to the NSView object in the Xib file at runtime. In your sample code above, you could create an instance of your own custom NSView subclass, and then add it to the existing view using the addSubView: method.
How about doing all the drawing in a simple subview and add it to the XIB view (in viewDidLoad)?

Resources