addSublayer not displaying image - ios

I'm having trouble getting an image in a CAlayer to display in an imageView. I can get it to work with a CAGradientLayer, but as soon as I change it to a CALayer with an image, nothing seems to work. It doesn't even display part of the image as I would expect it to, if the frame was shifted.
I've made sure the image name is correct and exists. For background: testImageView is the only imageView and covers the entire screen.
let strokeLayer = CALayer()
strokeLayer.contents = UIImage(named: "test.png")
strokeLayer.frame = CGRect(x: 0, y: 0, width: testImageView.frame.width, height: testImageView.frame.height)
testImageView.layer.addSublayer(strokeLayer)
Any help is appreciated!

Figured out I was just missing .cgImage at the end of the second line

If you want to display a image in CALayer you have to pass as
CGImage.strokeLayer.contents = UIImage(named: "test.png")?.cgImage

Related

Shape/mask on UIImage?

I was wondering how can this be achieved in Xcode? It's part of a profile, so the header is an actual UIImageView, but the curve below it, not sure how to achieve that. Any ideas?
Say that the grey area is built out of a bottom grey rectangle and on top another rectangle with your arc we could do something like this:
Create a UIBezierPath in the shape of a circle:
let path = UIBezierPath(ovalIn:CGRect(x: 0, y: view.bounds.height/2, width: view.bounds.width, height: view.bounds.height)).cgPath
Apply it to the top rectangle.
let overlay = CAShapeLayer()
overlay.path = path
overlay.fillColor = UIColor.gray.cgColor
overlay.shouldRasterize = true
view.layer.addSublayer(overlay)
This will create a perfect circle but you can tweak the CGRect's your liking in order to get the shape you want!
It can be achieved by giving header view UIBezierPath but if you don't want to do that stuff.
I found a cool way of doing.
Your Header view contain a image (Lets say it HeaderImage).
Make a Image of that shape (Lets say it MaskImage).
let path = UIImageView.init(image: #imageLiteral(resourceName: "MaskImage"))
Than apply this mask to Header Image.
HeaderImage.mask = path
Hope it work for you.

Cropping UIImage to custom path and keeping correct resolution?

I have a view (blue background...) which I'll call "main" here, on main I added a UIImageView that I then rotate, pan and scale. On main I have a another subview that shows the cropping area. Anything out of that under the darker area needs to be cropped.
I am trying to figure out how to properly create a cropped image from this state. I want the resulting image to look like this:
I want to make sure to keep the resolution of the image.
Any idea?
I have tried to figure out how to use the layer.mask property of the UIImageView. After some feedback, I think I could have another view (B) on the blue view, on B I would then add the image view, so then I would make sure that B's frame would match the rect of the cropping mask overlay. I think that could work? The only thing is I want to make sure I don't lose resolution.
So, earlier I tried this:
maskShape.frame = imageView.bounds
maskShape.path = UIBezierPath(rect: CGRect(x: 20, y: 20, width: 200, height: 200)).cgPath
imageView.layer.mask = maskShape
The rect was just a test rect and the image would be cropped to that path, but, I wasn't sure how to get a UIImage from all this that could keep the large resolution of the original image
So, I have implemented the method suggested by marco, it all works with the exception of keeping the resolution.
I use this call to take a screenshot of the view the contains the image and I have it clip to bounds:
public func renderToImage(afterScreenUpdates: Bool = false) -> UIImage {
let rendererFormat = UIGraphicsImageRendererFormat.default()
rendererFormat.opaque = isOpaque
let renderer = UIGraphicsImageRenderer(size: bounds.size, format: rendererFormat)
let snapshotImage = renderer.image { _ in
drawHierarchy(in: bounds, afterScreenUpdates: afterScreenUpdates)
}
return snapshotImage
}
The image I get is correct, but is not as sharp as the one I crop.
Hoe can I keep the resolution high?
In your view which keeps the image you must set clipsToBounds to true. Not sure if I got well but I suppose it's your "cropping area"

Cropping UI images

I have a SceneKit view that fills my screen. My goal is to let the user take snapshots of that scene, but the snapshots are not the whole screen, but an inset portion in a UIImageView which is slightly smaller than the screen. Ideally, the user should not notice, the image on top should be identical to the scene behind it.
I have coded this up using snapshot and cropped, but as you can see in the image, the scale ends up way off - see the width of the yellow line, and the size of the windows? It's also not positioned correctly, it's somewhat down and to the left from where it should be - the upper left should be below the line of windows, but you can see it is at the roofline above them. I can't see the original snapshot because the debugger QuickLook refuses to show it.
There's not much code to it, anyone see the problem:
let background = sceneView.snapshot().cgImage!
let cropped = background.cropping(to: overlayView.frame)
UIGraphicsBeginImageContextWithOptions(overlayView.frame.size, false, 1.0)
let context = UIGraphicsGetCurrentContext()
context!.setAlpha(0.50)
context!.draw(cropped!, in: overlayView.bounds)
let transparent = context!.makeImage();
UIGraphicsEndImageContext()
overlayView.image = UIImage.init(cgImage: transparent!, scale: 1.0, orientation: .downMirrored)
I have tried various scales and rects to no avail. I assume this is something very easy.
UPDATE: after several tries I was able to get quicklook to work. The snapshot is indeed the entire background as I would expect. But it is much larger than I would expect too - its 640, 998 while the cropped version is 228, 304. That explains the "zooming". This leads me to believe that the frame size of the inset view is NOT a direct relationship to the image size. Does that ring any bells? Is there some other rect I should be using rather than overlayView.frame?
So I assume the problem is that the frame coordinates are in one set of units and the image coordinates are in another. I was able to solve the problem this way:
let croprect = CGRect(x: overlayView.frame.origin.x * 2, y: overlayView.frame.origin.y * 2 - 45, width: overlayView.frame.width * 2, height: overlayView.frame.height * 2)
let drawrect = CGRect(x: 0, y: 0, width: overlayView.frame.width * 2, height: overlayView.frame.height * 2)
let background = sceneView.snapshot()
let cropped = background.cgImage!.cropping(to: croprect)
UIGraphicsBeginImageContextWithOptions(drawrect.size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
context!.setAlpha(0.50)
context!.draw(cropped!, in: drawrect)
let transparent = context!.makeImage();
UIGraphicsEndImageContext()
I'm extremely curious why I had to adjust the Y starting point to get them to line up, anyone have an idea?

iOS: how to scale up image nicely?

I'm having trouble scaling up an image in a nice way. I'm displaying an image in a UIImageView. But while the image is downloading from the server, I'd like to display a blurred version of the image. The server quickly gives me a small version of the image that looks like this:
Magnified, it looks like this:
I'd like to be able to draw it like this (which is how Chrome draws it when I scale the image up):
But when I put the image into a UIImageView and set imageview.layer.magnificationFilter to kCAFilterLinear or kCAFilterTrilinear, I get this:
... which is ugly - notice the hard bands of light and dark.
I've tried using CIFilter with CILanczosScaleTransform, and that looks promising, but I get an ugly border effect:
Well - any advice? Anybody done this nicely? I can try filters forever, thought I'd see if anybody's cracked this...
EDIT: I tried bbarnhart's suggestion. I get this - nicer, but the blur radius is too large or something:
EDIT 2: #bbarnhart, I can now get this, much closer!
Using the blur effect on your UIImageView might give you something closer to what you want.
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blurView.frame = yourImageView.bounds
yourImageView.addSubview(blurView)
Also check out bluuur
Update to include a playground:
import UIKit
import XCPlayground
let image = UIImage(named: "pixels")
let view = UIView(frame: CGRect(x: 0, y: 0, width: 346, height: 431))
let imageView = UIImageView(image: image)
let blurView = UIVisualEffectView(effect: UIBlurEffect(style: .Light))
blurView.frame = imageView.bounds
imageView.addSubview(blurView)
view.addSubview(imageView)
XCPlaygroundPage.currentPage.liveView = view

Sublayer is not showing up after add it

I'm trying to add the image "resizeLayer" over my UIView selectedShape by sublayering it over selectedShape
let sublayer = CALayer()
sublayer.bounds = selectedShape.bounds //even when inserted this line, sublayer still doesn't show up
sublayer.frame = selectedShape.frame
sublayer.contents = UIImage(named: "resizeLayer")
selectedShape?.layer.addSublayer(sublayer)
But when I run my code I don't see the layer at all
I even tried subviewing the image "resizeLayer" over the UIView "selectedShape"
let resizeFrame = UIImageView(image: UIImage(named: "resizeLayer"))
resizeFrame.frame = selectedShape.frame
resizeFrame.contentMode = UIViewContentMode.ScaleAspectFill
selectedShape.addSubview(resizeFrame)
But still, the "resizeLayer" does not show up!
It only shows up if I add the "resizeLayer" to the overall view:
let resizeFrame = UIImageView(image: UIImage(named: "resizeLayer"))
resizeFrame.frame = selectedShape.frame
resizeFrame.contentMode = UIViewContentMode.ScaleAspectFill
selectedShape.addSubview(resizeFrame)
self.view.insertSubview(resizeFrame, aboveSubview: selectedShape) //add this line
Any help on this would be really appreciated!
If it's relevant, this is how I made selectedShape
selectedShape = UIView(frame: CGRect(x: 0, y: 0, width: 60, height: 60))
selectedShape.layer.cornerRadius = 10
selectedShape.backgroundColor = UIColor.blueColor()
canvas.addSubview(selectedShape) //canvas is the view I'm adding selectedShape to
This is the image "resizeLayer" that I'm trying to add
The blue square is selectedShape. As you can see the layer is not showing up.
What I want to happen
You are using the entire frame for selectedShape. You should only use the width and the height because x and y should be zero. The image is added to selectedShape so the point (0,0) is the top left of the selectedShape view.
resizeFrame.frame = CGRect(origin: CGPointZero, size: selectedShape.frame.size)
I will admit that this stumped me for longer than it should have.
Not really an answer to his problem, but related and would have saved me time if somone had written this here before. So in hope to help someone and compile a more complete list of problems and their solution:
The addSublayer call needs to be made from the main-thread...
The funny thing is if you do it from a worker-thread it still shows up in the view-hirarchy-debugging with the content you set, but it's not displayed...
I think you should send selectedView to back:
canvas.sendSubviewToBack(selectedShape)

Resources