iOS - Using addSubView on a UIScrollView - objective-c

I am trying to use this bit of code:
[[myUIView layer] addSublayer: layer];
[myScrollView addSubview:myUIView];
[layer addAnimation:[self imagesAnimation] forKey:#"images"];
What I am doing to do is taking a layer that will later get a CAKeyFrameAnimate and placing that layer inside a UIView so I can use the standard view function:
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
return myUIView;
}
But I digress... Then placing the UIView & layer inside of a UIScrollView so I get all of that good UIScrollView functionality.
At least this is how I believe it to work atm.
Hoping for some help currently it just shows a white screen but when I replace the first to lines with:
[[myScrollView layer] addSublayer: layer];
I get an animation that plays but now zooming goodness.
Any help would be appreciated.
EDIT: In summary what I am trying to do is is have a CAKeyFrameAination inside of a layer inside of a UIView inside of a UIScrollView. The reason for this packing is because I need an Animation that can stop start and zoom in. the First three lines of code produce a blank white screen but when I replace them with the last bit where I'm putting the layer on myScrollView it plays but as expected I get no zooming as
-(UIView *)viewForZoomingInScrollView:(UIScrollView *)scrollView {
is expecting an UIView and doesn't work if I tell it to expect a layer.
Hope this is clearer.

I thought I was adding the UIView in via code but apparently I needed to add it in via the IB and link it up to the code.

Related

UI Code in viewDidLayoutSubViews doesn't render correctly at runtime

I have recently been battling with my view controller to set the correct dimensions of a border on a UIView.
*edit, this is for a static tableview (i am using it as a form). I am aware of rounding cells in a .xib and reusing the cell however I am not reusing cells, this approach would not be useful to me.
I have a screen with a static tableview which is embedded within a UIView and displayed on a main View Controller. I had been having problems with the border as if I set it anywhere before viewDidAppear my method to add the border would use the wrong dimensions and display the border incorrectly. Here is the method I'm using to add a border to my UIView:
- (void)createCellBorder: (UIView *)view container:(UIView *)container {
CALayer *borderLayer = [CALayer layer];
CGRect adjustedContainer = CGRectMake(0, 0, container.frame.size.width - 20, container.frame.size.height);
CGRect borderFrame = CGRectMake(0, 0, (adjustedContainer.size.width), (view.frame.size.height));
NSLog(#"VIEW DIMENSIONS FOR %# %f, %f",view , view.frame.size.width, view.frame.size.height);
[borderLayer setBackgroundColor:[[UIColor clearColor] CGColor]];
[borderLayer setFrame:borderFrame];
[borderLayer setCornerRadius:view.frame.size.height / 2];
[borderLayer setBorderWidth:1.0];
[borderLayer setBorderColor:[kTextColor2 CGColor]];
[view.layer addSublayer:borderLayer];
}
Note * To fix some bugs with a single cell having the wrong dimensions (always the final cell in the table) I am setting the width of the border layer based on the UITableView it is contained in.
Anyway I eventually found out that instead of using viewDidAppear I should be using viewDidLayoutSubviews to set my UI Geometry as at this point in time the view has calculated its dimensions. This works a treat and my cells will display with rounded borders... However there is a problem with the rendering itself.
First lets have a look at the cells being rounded from the viewDidAppear.
Everything looks fine, this is exactly how I want my cells to look (I have removed the content of them to demonstrate the borders only). Lets have a look at the code I'm using to round the cells.
-(void)viewDidAppear:(BOOL)animated {
[super viewDidAppear:animated];
[self configureCells];
}
- (void)configureCells {
[self createCellBorder:_contentCellOne container:_profileTable];
[self createCellBorder:_contentCellTwo container:_profileTable];
[self createCellBorder:_contentCellThree container:_profileTable];
[self createCellBorder:_contentCellFour container:_profileTable];
[self createCellBorder:_contentCellFive container:_profileTable];
[self createCellBorder:_contentCellSix container:_profileTable];
[self createCellBorder:_contentCellSeven container:_profileTable];
}
Now lets change where I call configureCells from and see the difference.
-(void)viewDidLayoutSubviews {
[super viewDidLayoutSubviews];
[self configureCells];
}
All that has changed is where I call this method from. And in theory this should be the right place to configure my UI (and certainly more optimal than calling from viewDidAppear).
As you can see there is some quite noticeable 'distortion' around the corners of the cells. The pictures provided are from the simulator but I have tried this on a device and get the same result (maybe looks worse on my phone).
I have no idea why this is happening, and it means that whilst I have got my code out of the viewDidAppear, its just as unusable as before. I assume there will be a rendering option I can tweak in the viewDidLayoutSubViews but can't seem to find anything. All suggestions greatly appreciated!
In my opinion, you should create a subclass for UITableViewCell and configure border of cells inside layoutIfNeeded method.
I have create a demo project, you can check it TableViewCellBorder.

Why NSView leaves an image on superview on where it was when I move it?

I am working on a small application on Mac that I need to create customed cursor and move it. I used NSImageView to implement it. However when I call setFrameOrigin (the same to setFrame) it will leaves images on the previous place.
Here is my code:
#property (nonatomic, strong) NSImageView *eraserView;
this is the define
_eraserView = [[NSImageView alloc] initWithFrame:CGRectMake(100, 100, 32, 32)];
_eraserView.image = [NSImage imageNamed:#"EraserCursor"];
[self.view addSubview:_eraserView];
[_eraserView setHidden:YES];
here is the initialization. Everything goes well until now but:
- (void)setImageatPoint:(NSPoint)point
{
[_eraserView setFrameOrigin:point];
}
- (void)hidePenImage
{
[_eraserView setHidden:YES];
}
- (void)unhidePenImage: (BOOL)isEraser
{
[_eraserView setHidden:NO];
}
These are methods I use to change the state of the NSImageView. They will be called by another class using delegate when corresponding events of trackpad occurs.
However every time I change the state of the NSImageView, it seems like it is drawn on the superview.
I debugged it and found there was no extra subviews. And when I use setHidden it has no effect on those tracks. I think it somehow did something to the CALayer, but I have no idea how to fix it.
Screenshots would help but in general if you move a view or change the area of the view that is drawn, you need to redraw.
To do this it kind of depends on how your drawing happens. Calling setNeedsDisplay may not be enough if your implementation of drawRect only draws a sub rect of the view bounds. Cocoa only draws what it is told to draw.
You can erase sections of the view that should be empty by drawing (filling) where it should be empty. That means drawing a color ( NSColor clearColor if nothing else) in the area that was previously drawn.

Draw Over Image

I'm working on some drawing code. I have that portion working great.
I want to draw over an image, but I want to still be able to see the detail of the image, the black lines, etc.
What I am working on is making a transparent UIImageView that holds the image.
I'm not sure how to get this set up properly though.
Should this be added above the other UIImageView that I color on or below it?
Here's what I have so far:
- (void)viewDidLoad
{
[super viewDidLoad];
topImageView = [[UIImageView alloc] initWithFrame:CGRectMake(0, 46, 320, 370)];
[topImageView setImage:[UIImage imageNamed:#"imagesmall.png"]];
topImageView.alpha = 1.0;
topImageView.layer.opacity = 1.0;
topImageView.layer.opaque = NO;
[self.view addSubview:topImageView];
[topImageView release];
}
Thoughts anyone?
Yes, you can draw views over other views. They are drawn in the order that they're added as subviews, unless you reorder them after that.
You may need to set the opaque property for some views (this is distinct from and overrides their layer opacity), and set their backgroundColor to nil. UIImageView seems to be transparent by default, as long as its image is; some other UIView subclasses are not.
So, just what is your overlay going to be? If you just need to display one image over another, what you have here seems to work already. If you need to draw some lines programmatically, you'll need to do this:
Create a subclass of UIView.
Implement its drawRect method to display the content you need.
When you add your custom view on top of the background image, make sure it is not opaque and has no backgroundColor.
A common problem here is to find that your foreground is working, but the background isn't being loaded properly. To make sure the background is there, set the alpha of the foreground view to 0.5. You won't want to do that in production, but it will allow you to verify that both views exist.

How to add a CALayer to an NSView on Mac OS X

I'm trying to learn how to use and implement CALayer in a Mac Objective-C application, but I can't seem to probably do the most basic thing - add a new layer and set its background colour/frame size. Can anyone see what is wrong with my code?
CALayer *layer = [CALayer layer];
[layer setFrame:CGRectMake(0, 0, 100, 100)];
[layer setBackgroundColor:CGColorCreateGenericRGB(1.0, 0.0, 0.0, 1.0)];
[self.layer addSublayer:layer];
[layer display];
I put this in the - (void)drawRect:(NSRect)rect method of my custom NSView subclass, but when I run the application, it just shows a blank view, with no background colour or evidence of the layer I created.
First of all, you don't want to add a layer in the drawRect: method of a view, this gets called automatically by the system and you'd probably end up with a lot more layers than you actually want. initWithFrame: or initWithCoder: (for views that are in a nib file) are better places to initialize your layer hierarchy.
Furthermore, NSViews don't have a root layer by default (this is quite different from UIView on iOS). There are basically two kinds of NSViews that use a layer: layer-backed views and layer-hosting views. If you want to interact with the layer directly (add sublayers etc.), you need to create a layer-hosting view.
To do that, create a CALayer and call the view's setLayer: method. Afterwards, call setWantsLayer:. The order is important, if you'd call setWantsLayer: first, you'd actually create a layer-backed view.
You need to make a call to the "setWantsLayer" method.
Check out the following documentation for the description for setWantsLayer:
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/ApplicationKit/Classes/NSView_Class/Reference/NSView.html
In a nutshell, your view needs to be layer-hosting view. Because it is a layer-hosting view, you should interact with the layer, and NOT interact with the view itself and don't add subviews to it.
[self setLayer:[CALayer new]];
[self setWantsLayer:YES]; // the order of setLayer and setWantsLayer is crucial!
[self.layer setBackgroundColor:[backgroundColor CGColor]];
Put this out of the drawRect. I normally put my layer setup in either the init method or the viewDidLoad.
Otherwise anytime the view is drawn a new layer is added and allocated. Also I've never used the [layer display] line before. The docs actually tell you not to call this method directly.
Updated info (Swift): first call view.makeBackingLayer() then set wantsLayer to true.
https://developer.apple.com/documentation/appkit/nsview/1483695-wantslayer

Background image for a window in Cocoa framework

I am looking for a perfect solution to set a background image for a window in a cocoa application. I haven't found a solution to this, I am new in objective c, so please anyone help me...
A window in Cocoa has a root-level view called the "content view". This is the view that contains all the others in a window. By default, it's just a plain, blank NSView. But you could easily create your own custom NSView subclass, override the drawRect: method to draw your background image, and use that for your custom view.
However, it might just be easier to use a plain old NSImageView. The advantage of this is that you can set, for example, autosizing behavior to keep the image pinned to one corner (try this with Installer.app by resizing the installer window). You would also be able to make it semi-opaque so that the background shows through a bit. (Again, I'm thinking of Installer.app; your app could be totally different)
Hope that gets you going in the right direction!
Michael Vannorsdel suggests sublassing NSView for the purpose, and I quote:
You'd really be better off making an
NSView subclass and having it draw
the image you want in drawRect:.
- (void)awakeFromNib
{
myImage = [[NSImage alloc] init....
[self setNeedsDisplay:YES];
}
- (void)drawRect:(NSRect)rect
{
NSSize isize = [myImage size];
[myImage drawInRect:[self bounds] fromRect:NSMakeRect(0.0, 0.0,
isize.width, isize.height) operation: NSCompositeCopy fraction:1.0];
}
Read that whole thread on cocoabuilder, it's quite instructive.