addSublayer doesn't add any sublayer on the view - ios

I was following a tutorial on a Core Animation book and I tried to do the following.
#interface ViewController ()
#property (nonatomic, weak) IBOutlet UIView * layerView;
#property (nonatomic, weak) CALayer *blueLayer;
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
self.blueLayer = [CALayer layer];
self.blueLayer.frame = CGRectMake(50.0f, 50.0f, 100.0f, 100.0f);
self.blueLayer.backgroundColor = [UIColor blueColor].CGColor;
[self.layerView.layer addSublayer: self.blueLayer];
}
...
#end
According to the book, the code above should print a blue layer on the screen in the middle on top of layerView. But, I can only see layerView and blueLayer is no where to be seen. Does anyone know what is happening? I'm lost.

You should be getting a warning on this line
self.blueLayer = [CALayer layer];
Since self.bluelayer is a weak pointer, ARC will discard the object as soon as it's created. The property should be declared strong.

Related

How can I get Automatically Refresh Views to work?

My custom UIView is not triggering this to run, but other custom views I make trigger it. Does anyone know the reason that this automatic refreshing of views would be triggered? Or Why "Refresh All Views" is greyed out.
Code is in here:
https://github.com/HannahCarney/HCRotaryWheel
Make sure you have IBInspectable properties for your view. (like
changing your view color,just to make sure your IBDesignable thing
works).
Make sure you have the code for your customisation in your view .m
file. (in your view draw rect method).
Make sure you provide the file owner name for your IBDesignable view.(in your storyboarD)
Make sure you mention IB_DESIGNABLE on your view .h file.
Finally a qucik clean and build of your storyboard,shall enable the
option of Refresh All views.
Sample code:
for view.h
#import <UIKit/UIKit.h>
IB_DESIGNABLE
#interface designalble_UIView : UIView
#property (nonatomic) IBInspectable UIColor *startColor;
#property (nonatomic) IBInspectable UIColor *midColor;
#property (nonatomic) IBInspectable UIColor *endColor;
#property (nonatomic) IBInspectable NSInteger borderWidth;
#property (nonatomic) IBInspectable CGFloat cornerRadious;
#property (nonatomic) IBInspectable BOOL isHorizontal;
#end
for view.m
#import "designalble_UIView.h"
#implementation designalble_UIView
- (instancetype)initWithFrame:(CGRect)frame {
self = [super initWithFrame:frame];
if (self) {
self.startColor = [UIColor redColor];
self.midColor = [UIColor greenColor];
self.endColor = [UIColor blueColor];
self.borderWidth = 2;
self.cornerRadious = 10;
self.isHorizontal = NO;
[self customInit];
}
return self;
}
- (id)initWithCoder:(NSCoder *)aDecoder {
self = [super initWithCoder:aDecoder];
if (self) {
[self customInit];
}
return self;
}
- (void)drawRect:(CGRect)rect {
[self customInit];
}
- (void)setNeedsLayout {
[super setNeedsLayout];
[self setNeedsDisplay];
}
- (void)prepareForInterfaceBuilder {
[self customInit];
}
- (void)customInit {
self.layer.cornerRadius = self.cornerRadious;
self.layer.borderWidth = self.borderWidth;
if (self.cornerRadious > 0) {
self.layer.masksToBounds = YES;
}
CAGradientLayer *gradient = [CAGradientLayer layer];
gradient.frame = self.bounds;
gradient.colors = [NSArray arrayWithObjects:(id)[self.startColor CGColor],(id)[self.midColor CGColor], (id)[self.endColor CGColor], nil];
gradient.endPoint = (self.isHorizontal) ? CGPointMake(1, 0) : CGPointMake(0, 1);
[self.layer insertSublayer:gradient atIndex:0];
}
I solved this because I had put IB_DESIGNABLE in the wrong spot in my code:
I had a protocol at the top of my view and previously it looked like
IB_DESIGNABLE
#protocol RotaryProtocol <NSObject>
- (void) wheelDidChangeValue:(int)currentSector;
#end
#interface HCRotaryWheel : UIView
#property (weak) id <RotaryProtocol> delegate;
#property (nonatomic, strong) IBInspectable UIColor* background;
#end
To fix this I had to move IB_DESIGNABLE below protocol
#protocol RotaryProtocol <NSObject>
- (void) wheelDidChangeValue:(int)currentSector;
#end
IB_DESIGNABLE
#interface HCRotaryWheel : UIView
#property (weak) id <RotaryProtocol> delegate;
#property (nonatomic, strong) IBInspectable UIColor* background;
#end
Now my views refresh!!

UIView is never rendered if added as subview in viewDidLoad

I'm trying to add a subview to a view controller programatically, but it seems like if I add the subview in the viewDidLoad of the view controller whatever I have added as subview in the view itself is not rendered in the view controller.
But if I move the [self.view addSubview:myUIView] in the init method of the view controller, everything is rendered.
Also, if I call the same methods in the viewDidAppear I get all the elements rendered, but it's right after the view controller was displayed, and I can see when the elements are rendered in the view controller.
This would be my view controller:
//interface
#class RLJSignInView;
#protocol RLJSignInViewControllerDelegate;
#interface RLJSignInViewController : UIViewController <UITextFieldDelegate>
#property (nonatomic, readwrite) RLJSignInView *signInView;
#property (nonatomic, assign) id<RLJSignInViewControllerDelegate> delegate;
#end
//implementation
#import "RLJSignInViewController.h"
#import "RLJSignInView.h"
#import "RLJSignInViewControllerDelegate.h"
#import "UIColor+RLJUIColorAdditions.h"
#implementation RLJSignInViewController
- (id)init
{
self = [super init];
if (self) {
_signInView = [[RLJSignInView alloc] initWithFrame:self.view.bounds];
[self.view addSubview:self.signInView];
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
NSLog(#"%#", self.signInView);
[self.view setBackgroundColor:[UIColor rgbWithRed:250 green:250 blue:250]];
}
#end
And this would be my view:
//interface
#interface RLJSignInView : UIView
#property (strong, nonatomic, readwrite) UITextField *username;
#property (strong, nonatomic, readwrite) UITextField *password;
#property (strong, nonatomic, readwrite) UIButton *signIn;
#property (strong, nonatomic, readwrite) UIButton *signUp;
#end
//implementation
#import "RLJSignInView.h"
#import "UITextField+RLJUITextFieldAdditions.h"
#import "UIButton+RLJUIButtonAdditions.h"
#implementation RLJSignInView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
CGRect usernameInputFrame = CGRectMake(10.0, 40.0, self.bounds.size.width - 20, 40);
CGRect passwordInputFrame = CGRectMake(10.0, 79.0, self.bounds.size.width - 20, 40);
CGRect signInButtonFrame = CGRectMake(10.0, 160, self.bounds.size.width - 20, 40);
CGRect signUpButtonFrame = CGRectMake(10.0, 220, self.bounds.size.width - 20, 40);
UIView *usernameInputLeftView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 40)];
UIView *passwordInputLeftView = [[UIView alloc] initWithFrame:CGRectMake(0.0, 0.0, 10.0, 40)];
self.username = [[UITextField alloc] initWithFrame:usernameInputFrame
textAlignment:NSTextAlignmentLeft
textColor:[UIColor blackColor]
clearButton:UITextFieldViewModeWhileEditing
leftView:usernameInputLeftView
placeholder:#"Username"
backgroundColor:[UIColor whiteColor]
strokeWidth:2.0
strokeColor:[UIColor lightGrayColor]
keyboardType:UIKeyboardTypeEmailAddress
byRoundingCorners:UIRectCornerTopLeft | UIRectCornerTopRight
cornerRadii:CGSizeMake(4.0, 4.0)
secure:NO];
self.password = [[UITextField alloc] initWithFrame:passwordInputFrame
textAlignment:NSTextAlignmentLeft
textColor:[UIColor blackColor]
clearButton:UITextFieldViewModeWhileEditing
leftView:passwordInputLeftView
placeholder:#"Password"
backgroundColor:[UIColor whiteColor]
strokeWidth:2.0
strokeColor:[UIColor lightGrayColor]
keyboardType:UIKeyboardTypeDefault
byRoundingCorners:UIRectCornerBottomLeft | UIRectCornerBottomRight
cornerRadii:CGSizeMake(4.0, 4.0)
secure:YES];
self.signIn = [[UIButton alloc] initWithFrame:signInButtonFrame
title:#"Sign In"
colorNormal:[UIColor whiteColor]
colorHighlighted:[UIColor whiteColor]
colorDisabled:[UIColor whiteColor]
backgroundNormal:[UIColor colorWithRed:82 / 255.0 green:156 / 255.0 blue:201 / 255.0 alpha:1.0]
cornerRadius:4.0];
self.signUp = [[UIButton alloc] initWithFrame:signUpButtonFrame
title:#"Sign Up"
colorNormal:[UIColor blackColor]
colorHighlighted:[UIColor blackColor]
colorDisabled:[UIColor blackColor]
backgroundNormal:[UIColor whiteColor]
cornerRadius:4.0];
[self addSubview:self.username];
[self addSubview:self.password];
[self addSubview:self.signIn];
[self addSubview:self.signUp];
}
return self;
}
#end
I'm not sure what I could do about it, but I know for sure that I would not like the view to be added to the subview on initialisation.
Or maybe I'm doing the rendering of the subviews of the view wrong. I would appreciate some input on this matter if anyone encountered the same thing or if noticed that I messed up something.
Don't call self.view from inside your init method. This will trigger viewDidLoad being called before your init method even returns, which is incorrect behavior.
Instead, follow this pattern:
create your objects (properties / instance variables) in init, or by using lazy instantiation
add the subviews in viewDidLoad
set the frames in viewWillLayoutSubviews
This pattern will avoid bugs like the one you posted here, and set you up for success handling rotation and resizing events in the future.
The key might be you are using self.view.bounds too early. It might work when you provide an initial size frame (like IB would do).
init is too early. The view controller is initializing, the view is not loaded yet, which means there's no view and the bounds will return CGRectZero.
viewDidLoad is also too early to rely on the view's bounds. The view is just loaded, it does not have a superview yet and might be resized later.
viewWillAppear is the first moment you can rely on the view's bounds. Since it will be resized according to the autoresizingmasks or autolayout constraints.
You can add the subview in viewDidLoad but you you should provide an initial frame (and you set the correct autoresizingmasks or constraints) it should all work fine.

Get the current value of UISlider in drawRect: method

I'm trying to move a point drawn in a UIView based on the value of a UISlider. The code below is for a UIView (subview ?) with a custom class (WindowView) on a UIViewController.
WindowView.h
#import <UIKit/UIKit.h>
#interface WindowView : UIView
- (IBAction)sliderValue:(UISlider *)sender;
#property (weak, nonatomic) IBOutlet UILabel *windowLabel;
#end
WindowView.m
#import "WindowView.h"
#interface WindowView ()
{
float myVal; // I thought my solution was using an iVar but I think I am wrong
}
#end
#implementation WindowView
#synthesize windowLabel;
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
- (void)sliderValue:(UISlider *)sender
{
myVal = sender.value;
windowLabel.text = [NSString stringWithFormat:#"%f", myVal];
}
- (void)drawRect:(CGRect)rect
{
// I need to get the current value of the slider in drawRect: and update the position of the circle as the slider moves
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(myVal, myVal, 10, 10)];
[circle fill];
}
#end
OK, you need to store the slider value in an instance variable and then force the view to redraw.
WindowView.h:
#import <UIKit/UIKit.h>
#interface WindowView : UIView
{
float _sliderValue; // Current value of the slider
}
// This should be called sliderValueChanged
- (IBAction)sliderValue:(UISlider *)sender;
#property (weak, nonatomic) IBOutlet UILabel *windowLabel;
#end
WindowView.m (modified methods only):
// This should be called sliderValueChanged
- (void)sliderValue:(UISlider *)sender
{
_sliderValue = sender.value;
[self setNeedsDisplay]; // Force redraw
}
- (void)drawRect:(CGRect)rect
{
UIBezierPath *circle = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(_sliderValue, _sliderValue, 10, 10)];
[circle fill];
}
You probably want to initialise _sliderValue to something useful in the view's init method.
Also _sliderValue probably isn't the name you want to choose; perhaps something like _circleOffset or some such.

UIView setNeedsDisplay doesn't work

Simple code is here:
Custom MyButton.h
#interface PaiLifeReadingListButton : UIButton
#property (strong, nonatomic) UIImageView *logoImageView;
#end
Custom MyButton.m
#implementation PaiLifeReadingListButton
#synthesize logoImageView = _logoImageView;
-(id)init
{
_logoImageView = [[UIImageView alloc] initWithFrame:CGRectMake(33.0, 20.0, 80.0, 80.0)];
[_logoImageView setImage:[UIImage imageNamed:#"pic"]];
[self addSubview: _logoImageView];
}
#end
Custom MyView.h:
#property (strong, nonatomic) Mybutton *mybutton;
Custom MyView.m:
-(id) init
{
myButton = [[MyButton alloc] init];
[self addSubview:myButton];
}
ViewController.m:
#synthesize myView;
- (void)viewDidLoad
{
myView = [[Myview alloc] init];
[myView.myButton addTarget:self action:#selector(pressed:) forControlEvents:UIControlEventTouchDown];
self.view = myView;
}
-(void)pressed : (MyButton *)button
{
UIImageView *newImageView = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"other-pic"] highlightedImage:nil];
myView.myButton.logImageView = newImageView;
[self.view setNeedsDisplay]; //----this does not work
}
When I press the button, the imageview does not change. How can I fix this?
Thank you very much!
First thing , you don't need setNeedsDisplay here. setNeedsDisplay is used for redrawing the view (it internally calls the drawRect: method of the view if required) which is not what you want.
Second, you have given the class of your button as MyButton,but in the actual .h and .m files, it is given as #interface PaiLifeReadingListButton and #implementation PaiLifeReadingListButton respectively.
Another thing, the view and button is not initialized with a frame. But I assume you have done that already, since your only problem is the image view not changing.
In your code in pressed method you should change views in the hierarchy, but you change only the object member. You can do something like
myView.myButton.logImageView.image = [UIImage imageNamed:#"other-pic"];

Cannot access UIColor property of UIView from UIViewController

Trying to set a UIColor variable on a UIView subclass from the main UIViewController. I have a property for retrieving the color that is requested in the UIViewController and I have a property for the color to receive on the UIView. However, when I use the dot method to apply the retrieved color I get an error "Property "brushColor" no found on object of type 'UIView *'"
//Controller.h
#interface ViewController : UIViewController {
UIColor *colorPicked;
UIView *drawScreen;
}
#property (nonatomic, strong) UIView *drawScreen;
#property (nonatomic, strong) UIColor *colorPicked;
//Controller.m
#implementation ViewController
#synthesize drawScreen;
#synthesize colorPicked;
- (void)viewDidLoad{
self.drawScreen = [[DrawingView alloc] initWithFrame:CGRectMake(0, 44, 1024, 724)];
drawScreen.backgroundColor = [UIColor clearColor];
drawScreen.brushColor = self.colorPicked; //This line errors stating the property is not available on type (UIView *)
[self.view addSubview:drawScreen];
[super viewDidLoad];
}
//View.h
#interface DrawingView : UIView{
UIColor *brushColor;
UIBezierPath *myPath;
}
#property (nonatomic, retain) UIColor *brushColor;
#end
//View.m
#implementation DrawingView
#synthesize brushColor;
- (void)drawRect:(CGRect)rect{
myPath = [[UIBezierPath alloc] init];
[brushColor setStroke];
[myPath strokeWithBlendMode:kCGBlendModeNormal alpha:1.0];
}
#end
you have declared drawScreen as UIView, change its declaration in the .h to
#property (nonatomic, strong) DrawingView *drawScreen;
and change the ivar declaration to match (or remove it altogether since you're using properties anyway)

Resources