Sublayer is not visible unless added after viewDidAppear - uiimageview

I am trying to load a profile image for an app, but I want to add a white border to it and make it circular before I display it. In order to do that I set the image property on the UIImageView and then prepare the image by setting the appropriate corner radius, masking it to the bounds and then adding a white border sublayer. Originally, I was loading the image in over the internet asynchronously and everything worked fine because the image appeared after the view had appeared. However, when I cached the image and tried to prepare it before the imageView was shown on screen I couldn't get the border sublayer to display. It still masked properly, just no border. The code that I am using to prepare the image is below.
- (void)prepareProfileImage
{
CALayer *imageLayer = self.profileImageView.layer;
imageLayer.borderColor = [UIColor whiteColor].CGColor;
imageLayer.allowsEdgeAntialiasing = NO;
[imageLayer setCornerRadius:PROFILE_IMAGE_DIAMETER/2.0];
[imageLayer setMasksToBounds:YES];
CALayer *borderLayer = [CALayer layer];
CGRect borderFrame = CGRectMake(-1.0, -1.0, (self.profileImageView.frame.size.width+2.0), (self.profileImageView.frame.size.height+2.0));
[borderLayer setBackgroundColor:[[UIColor whiteColor] CGColor]];
[borderLayer setFrame:borderFrame];
[borderLayer setCornerRadius:PROFILE_IMAGE_DIAMETER/2.0];
[borderLayer setBorderWidth:PROFILE_IMAGE_BORDER_WIDTH+1.0];
[borderLayer setBorderColor:[UIColor whiteColor].CGColor];
[imageLayer addSublayer:borderLayer];
}
This method works fine as long as it's called after viewDidLoad, but will not add the border layer if called before viewDidLoad. I have confirmed that self.profileImageView has been allocated at the time of this method call, but I only get the circular image, no border.
Is there something that I am misunderstanding about CALayers? Should it matter when I add the layer as a sublayer?
The reason I am not using the border property directly on the image layer is that it leaves a tiny sliver of image around the outside that is displeasing.

I have found the answer! The problem was that I want setting the frame of the border layer using the profileImageView frame, but because I am using autolayout that property is not set until after the view is displayed on screen.
I ended up doing:
/*! This function adds a border layer to the profile image view. */
- (void)addBorderLayerToProfileImageView {
CALayer *borderLayer = [CALayer layer];
CGRect borderFrame = CGRectMake(-1.0, -1.0, (PROFILE_IMAGE_DIAMETER+2.0), (PROFILE_IMAGE_DIAMETER+2.0));
[borderLayer setBackgroundColor:[[UIColor clearColor] CGColor]];
[borderLayer setFrame:borderFrame];
[borderLayer setCornerRadius:PROFILE_IMAGE_DIAMETER/2.0];
[borderLayer setBorderWidth:PROFILE_IMAGE_BORDER_WIDTH+1.0];
[borderLayer setBorderColor:[UIColor whiteColor].CGColor];
[self.profileImageView.layer addSublayer:borderLayer];
}
The reason I was adding a border layer at all is because the border on the layer leaves a small artifact between the edge of the image view and the border.

I was having the same issue as you: sublayer not being visible unless I called it on viewDidAppear.
I saw your answer but it did not work for me. I am using AutoLayout so I needed a solution that would be dynamic, not knowing my height or width of my view. My frame.size of my view I was trying to add a layer to would has a height and width value of 0 until viewDidAppear (makes sense).
After learning more about the lifecycle of UIView and UIViewController, the solution for me was to use the layoutSubviews() function of my custom UIView to add the subview to it as AutoLayout has measured the views at that point.
You can also add layers to subviews in a UIViewController in the viewDidLayoutSubviews() function as AutoLayout has measured by this point as well.

Related

NSView layer shadow draws on subviews, not view itself

I'm trying to set a shadow on the layer of an NSView, but the layer is being drawn on the subviews of my view, not the view itself. What can be causing this?
The view in question and all its subviews are layer-backed. All but one of the subviews are buttons (NSButton) with images - the other one is a custom view that renders via drawRect:.
self.layer.shadowColor = [[NSColor blackColor] colorWithAlphaComponent:0.9].CGColor;
self.layer.shadowRadius = 2;
self.layer.shadowOffset = NSMakeSize(0, -1);
self.layer.shadowOpacity = 0.9;
I've also tried setting the shadow via an NSShadow, with the same outcome.
Turns out that if you set a shadow on an NSView's layer, it only draws on that view if the view has a drawRect: implementation that draws something non-transparent. Otherwise it draws on all the view's subviews.
Weird.

How can I rotate image after face detection? iOS

I tried rotate image and view, only view or only image, but I have not got view that I need. I want just image with faces on the top of the screen, I can rotate a window, but I use my code not in AppDelegate and also I not want to rotate other elements, my code here, it rotate all view .
-(void)markFaces:(UIImageView *)facePicture
{
// draw a CI image with the previously loaded face detection picture
CIImage* image = [CIImage imageWithCGImage:facePicture.image.CGImage];
// create a face detector - since speed is not an issue we'll use a high accuracy
// detector
CIDetector* detector = [CIDetector detectorOfType:CIDetectorTypeFace
context:nil options:[NSDictionary dictionaryWithObject:CIDetectorAccuracyHigh forKey:CIDetectorAccuracy]];
// create an array containing all the detected faces from the detector
NSArray* features = [detector featuresInImage:image];
// we'll iterate through every detected face. CIFaceFeature provides us
// with the width for the entire face, and the coordinates of each eye
// and the mouth if detected. Also provided are BOOL's for the eye's and
// mouth so we can check if they already exist.
for(CIFaceFeature* faceFeature in features)
{
// get the width of the face
CGFloat faceWidth = faceFeature.bounds.size.width;
// create a UIView using the bounds of the face
UIView* faceView = [[UIView alloc] initWithFrame:faceFeature.bounds];
// add a border around the newly created UIView
faceView.layer.borderWidth = 1;
faceView.layer.borderColor = [[UIColor redColor] CGColor];
faceView.backgroundColor = [UIColor blackColor];
// add the new view to create a box around the face
//[faceView setTransform:CGAffineTransformMakeScale(-1, -1)];
[self.view addSubview:faceView];
[self.view setTransform:CGAffineTransformMakeScale(-1, -1)]; // !!!!
}
-(void)faceDetector
{
// Load the picture for face detection
UIImageView* image = [[UIImageView alloc] initWithImage:[UIImage imageNamed:#"test1.jpeg"]];
// Draw the face detection image
[self.view addSubview:image];
// Execute the method used to markFaces in background
[self performSelectorInBackground:#selector(markFaces:) withObject:image];
// flip image on y-axis to match coordinate system used by core image
[image setTransform:CGAffineTransformMakeScale(1, -1)];//!!
}

NSTableView with rounded corners

I'm trying to customize the UI of my application and I want my NSTableView to have rounded corners. So I subclassed NSTableView and got this:
However, when I populate the table and select a row, the selection is drawn over the border like this:
I've tried adding a clip in the table view drawing code and it doesn't work. Any suggestions for how I can fix this?
Edit:
My drawing code in the NSTableView is the following:
- (void)drawRect:(NSRect)dirtyRect {
[NSGraphicsContext saveGraphicsState];
NSRect frame = NSMakeRect(0.0, 0.0, [self bounds].size.width, [self bounds].size.height-1.0);
[[NSBezierPath bezierPathWithRoundedRect:frame xRadius:3.6 yRadius:3.6] addClip];
[super drawRect:dirtyRect];
[NSGraphicsContext restoreGraphicsState];
}
The actual rounded frame is drawn in the NSScrollView drawRect method. The interesting thing is that this does clip the selection of the very first and very last rows:
But not when the table is scrolling:
So the question remains: how can I clip all drawing inside the rounded frame of the NSScrollView?
I found that you can call this on the container scroll view of the table view.
self.scrollView.wantsLayer = TRUE;
self.scrollView.layer.cornerRadius = 6;
That's all I needed and it works. No subclassing needed.
I was able to solve this pretty nicely using CALayer. After trying subclassing everything from NSScrollView to NSTableView to NSClipView, and still getting the rendering problems shown above, I finally simply added this code to the drawRect of the NSScrollView subclass:
if (!self.contentView.wantsLayer) {
[self.contentView setWantsLayer:YES];
[self.contentView.layer setCornerRadius:4.0f];
}
And then I draw the frame in the same drawRect method of the NSScrollView. It solves all the problems above.

How to shadow documentView in NSScrollView?

How to shadow documentView in NSScrollView?
The effect look likes iBook Author:
You need to inset the content in your document view to allow space for the shadow to be displayed, then layer back the view and set a shadow on it. Example:
view.wantsLayer = YES;
NSShadow *shadow = [NSShadow new];
shadow.shadowColor = [NSColor blackColor]
shadow.shadowBlurRadius = 4.f;
shadow.shadowOffset = NSMakeSize(0.f, -5.f);
view.shadow = shadow;
The NSScrollView contentView is an NSView subclass, which has a shadow field, if you create a shadow object and assign it to this field, the view will automatically show a drop shadow when drawn
NSShadow* shadow = [[NSShadow alloc] init];
shadow.shadowBlurRadius = 2; //set how many pixels the shadow has
shadow.shadowOffset = NSMakeSize(2, -2); //the distance from the view the shadow is dropped
shadow.shadowColor = [NSColor blackColor];
self.scrollView.contentView.shadow = shadow;
This works because all views when are drawn on drawRect use this shadow property by using [shadow set].
doing [shadow set] during a draw operation makes whatever is drawn after that to be replicated underneath
I'm new to entering posts on stack overflow but I had the same issue and have solved it so I thought after searching the net for hours to find a solution it would be nice to answer it.
My solution is to create a subclass for NSClipView with the following code for drawRect...
- (void)drawRect:(NSRect)dirtyRect
{
[super drawRect:dirtyRect];
NSRect childRect = [[self documentView] frame];
[NSGraphicsContext saveGraphicsState];
// Create the shadow below and to the right of the shape.
NSShadow* theShadow = [[NSShadow alloc] init];
[theShadow setShadowOffset:NSMakeSize(4.0, -4.0)];
[theShadow setShadowBlurRadius:3.0];
// Use a partially transparent color for shapes that overlap.
[theShadow setShadowColor:[[NSColor grayColor]
colorWithAlphaComponent:0.95f]];
[theShadow set];
[[self backgroundColor] setFill];
NSRectFill(childRect);
// Draw your custom content here. Anything you draw
// automatically has the shadow effect applied to it.
[NSGraphicsContext restoreGraphicsState];
}
You then need to create an instance of the subclass and set it with the setContentView selector.
You also need to repaint the clip view every time the content view size changes. If you have your content view set up to change in terms of canvas size when the user wants then unless you repaint the clip view some nasty shadow marks will left behind.
You don't need to mess about with clips as others have suggested.
Hope it helps!

CALayer, anchorPoint and layer hierarchy

I try to use the anchorPoint in a layer hierarchy to move a layer with its sublayers. Unfortunately the sublayer did not move together with the root layer. In a custom NSView is set up my layer hierarchy like in the following snippet.
CALayer * rootLayer;
rootLayer = [[CALayer layer] retain];
rootLayer.position = CGPointMake(...);
[self.layer addSublayer:rootLayer];
subLayer = [[MapLayer layer] retain];
subLayer.position = CGPointMake(...);
[rootLayer addSublayer:baseLayer];
While handling a mouse event I want to set the anchorPoint of the rootLayer to move the whole layer hierarchy:
rootLayer.anchorPoint = CGPoint(0.7, 0.7);
I expect from this call, that the rootLayer moves together with its subLayer so that the anchor point is at the center of the view. What happens is, that the sublayer did not move. Only when I call:
rootLayer.anchorPoint = CGPoint(0.7, 0.7);
subLayer.anchorPoint = CGPoint(0.7, 0.7);
the layers behave as expected.
I thought setting the anchor point of the root layer would be enough. This is used in my Map application for OS X. There I set up the view and use the anchorPoint to move the whole map. With only one sublayer in the custom NSView the application behaves as expected.
Thanks in advance.
UPDATE: After a discussion with a colleague I set the bounds of the rootLayer and now it works.
CALayer * rootLayer;
rootLayer = [[CALayer layer] retain];
rootLayer.bounds = CGRectMake(0, 0, 256, 256);
rootLayer.position = CGPointMake(...);
[self.layer addSublayer:rootLayer];
// ...

Resources