how Add mirror image to 2d Box corners - ios

hello friends i need to add mirror image in right corner and bottom.
i create the type of view with the help of this answer
how to create a view like this shape in swift?
but i am not able to add image in right corner and bottom

Current approach is somewhat different from the previous one.
Previously i've drawn the right and bottom corner and resized the image such that, the appearance is asked in that question.
But in this question, that approach won't work anymore. First reason is that draw(in rect: CGRect) does not provide mirroring functionality for image while drawing. iOS only provide mirroring functionality while drawing in UIImageView. So to do the mirroring we need to setup 3 image view.
So the approach to achieve it is following
Put one UIImageView in center.
Put one UIImageView in the right of the center another to the bottom.
Now calculate the right mirrored image and apply shear the right image view.
Do the same for the bottom.
One problem still remain in the approach described above. For example we shear the right image view according to y axis. The shear operation works along with center. So the left side and right side both shears across the y axis. So we translate positive to the x axis so that all the shear applies to the right of UIImageView. Thats why overlap the right and main image view to fill the gap between to, as following
rightImageView.leadingAnchor.constraint(equalTo: mainImageView.trailingAnchor, constant: -stripSize / 2),
Same goes for the bottom image view.
Code
lass ViewController: UIViewController {
let mainImageView: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
let rightImageView: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
let bottomImageView: UIImageView = {
let view = UIImageView()
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
return view
}()
let rightDarkView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.4)
return view
}()
let bottomDarkView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
view.backgroundColor = UIColor.init(red: 0, green: 0, blue: 0, alpha: 0.5)
return view
}()
let mainImageSize = CGSize(width: 240, height: 240)
let stripSize = CGFloat(20)
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupView()
setupMirroView()
}
func setupView() {
view.addSubview(mainImageView)
view.addSubview(rightImageView)
view.addSubview(bottomImageView)
view.addSubview(rightDarkView)
view.addSubview(bottomDarkView)
NSLayoutConstraint.activate([
mainImageView.centerXAnchor.constraint(equalTo: view.centerXAnchor),
mainImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor),
mainImageView.widthAnchor.constraint(equalToConstant: mainImageSize.width),
mainImageView.heightAnchor.constraint(equalToConstant: mainImageSize.height),
rightImageView.leadingAnchor.constraint(equalTo: mainImageView.trailingAnchor, constant: -stripSize / 2),
rightImageView.topAnchor.constraint(equalTo: mainImageView.topAnchor),
rightImageView.bottomAnchor.constraint(equalTo: mainImageView.bottomAnchor),
rightImageView.widthAnchor.constraint(equalToConstant: stripSize),
rightDarkView.leadingAnchor.constraint(equalTo: rightImageView.leadingAnchor),
rightDarkView.topAnchor.constraint(equalTo: rightImageView.topAnchor),
rightDarkView.trailingAnchor.constraint(equalTo: rightImageView.trailingAnchor),
rightDarkView.bottomAnchor.constraint(equalTo: rightImageView.bottomAnchor),
bottomImageView.topAnchor.constraint(equalTo: mainImageView.bottomAnchor, constant: -stripSize / 2),
bottomImageView.leadingAnchor.constraint(equalTo: mainImageView.leadingAnchor),
bottomImageView.trailingAnchor.constraint(equalTo: mainImageView.trailingAnchor),
bottomImageView.heightAnchor.constraint(equalToConstant: stripSize),
bottomDarkView.leadingAnchor.constraint(equalTo: bottomImageView.leadingAnchor),
bottomDarkView.topAnchor.constraint(equalTo: bottomImageView.topAnchor),
bottomDarkView.trailingAnchor.constraint(equalTo: bottomImageView.trailingAnchor),
bottomDarkView.bottomAnchor.constraint(equalTo: bottomImageView.bottomAnchor)
])
}
func setupMirroView() {
let image = UIImage(named: "image")
mainImageView.image = image
// prepare the image for the right image view
let rightImage = image?.cropped(to: CGSize(width: stripSize, height: mainImageSize.height),
drawInto: CGRect(x: stripSize - mainImageSize.width, y: 0, width: mainImageSize.width, height: mainImageSize.height))
let rightImageMirrored = UIImage(cgImage: rightImage!.cgImage!, scale: 1.0, orientation: .upMirrored)
rightImageView.image = rightImageMirrored
var rightTransform = CGAffineTransform.identity
rightTransform = rightTransform.translatedBy(x: stripSize / 2, y: 0)
rightTransform = rightTransform.concatenating(CGAffineTransform(a: 1.0, b: 1.0, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0))
rightImageView.transform = rightTransform
rightDarkView.transform = rightTransform
// prepare the image for the left image view
let downImage = image?.cropped(to: CGSize(width: mainImageSize.width, height: stripSize),
drawInto: CGRect(x: 0, y: stripSize - mainImageSize.height, width: mainImageSize.width, height: mainImageSize.height))
let downImageMirroed = UIImage(cgImage: downImage!.cgImage!, scale: 1.0, orientation: .downMirrored)
bottomImageView.image = downImageMirroed
var downTransform = CGAffineTransform.identity
downTransform = downTransform.translatedBy(x: 0, y: stripSize / 2)
downTransform = downTransform.concatenating(__CGAffineTransformMake(1.0, 0.0, 1.0, 1.0, 0.0, 0.0))
bottomImageView.transform = downTransform
bottomDarkView.transform = downTransform
}
}
extension UIImage {
func cropped(to size: CGSize, drawInto: CGRect) -> UIImage {
UIGraphicsBeginImageContext(size)
self.draw(in: drawInto)
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
output

One approach is to use 3 image views - the "main" imageView plus a right-side imageView and a bottom imageView.
Scale your image to fit the main view + a small amount for the right-side and bottom.
Set the main imageView's .contentMode = .topLeft to clip the right and bottom.
Set the right-side imageView to .topRight to clip the left and bottom.
Set the bottom imageView to .leftBottom to clip the top and right.
Darken the image for the right-side and bottom views (to make it look a little "shadowed"
Then, apply CGAffineTransform to skew the right-side and bottom views.
Using this image (3:2 ratio):
and this code (everything is done via code - no IBOutlets needed):
import UIKit
import CoreImage
class ImageWorkViewController: UIViewController {
let mainImageView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.clipsToBounds = true
v.contentMode = .topLeft
return v
}()
let rightImageView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.clipsToBounds = true
v.contentMode = .topRight
return v
}()
let bottomImageView: UIImageView = {
let v = UIImageView()
v.translatesAutoresizingMaskIntoConstraints = false
v.clipsToBounds = true
v.contentMode = .bottomLeft
return v
}()
// this will be the width of the skewed right-side and height of the skewed bottom
let vDepth:CGFloat = 10.0
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(mainImageView)
view.addSubview(rightImageView)
view.addSubview(bottomImageView)
NSLayoutConstraint.activate([
// constrain main image view 40-pts from each side
mainImageView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor, constant: 40.0),
mainImageView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor, constant: -40.0),
// centered vertically
mainImageView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0.0),
// use 3:2 ratio
mainImageView.heightAnchor.constraint(equalTo: mainImageView.widthAnchor, multiplier: 2.0 / 3.0),
// constrain right image view to main image view
// right-edge
// top + 1/2 of vDepth
// equal height
// width = vDepth
rightImageView.leadingAnchor.constraint(equalTo: mainImageView.trailingAnchor, constant: 0.0),
rightImageView.topAnchor.constraint(equalTo: mainImageView.topAnchor, constant: vDepth / 2.0),
rightImageView.heightAnchor.constraint(equalTo: mainImageView.heightAnchor, multiplier: 1.0),
rightImageView.widthAnchor.constraint(equalToConstant: vDepth),
// constrain bottom image view to main image view
// left-edge + 1/2 of vDepth
// bottom
// equal width
// height = vDepth
bottomImageView.leadingAnchor.constraint(equalTo: mainImageView.leadingAnchor, constant: vDepth / 2.0),
bottomImageView.topAnchor.constraint(equalTo: mainImageView.bottomAnchor, constant: 0.0),
bottomImageView.widthAnchor.constraint(equalTo: mainImageView.widthAnchor, multiplier: 1.0),
bottomImageView.heightAnchor.constraint(equalToConstant: vDepth),
])
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// run this on viewDidLayoutSubviews() so we have valid frame sizes
if let sourceImg = UIImage(named: "goal3x2") {
// resize image to width and height of main image view, plus vDepth value
let mainImg = resizeImage(image: sourceImg, newSize: CGSize(width: mainImageView.frame.width + vDepth, height: mainImageView.frame.height + vDepth))
// set the main image
mainImageView.image = mainImg
// we're going to darken the right-side and bottom images a little bit
if let currentFilter = CIFilter(name: "CIColorControls") {
let context = CIContext(options: nil)
let beginImage = CIImage(image: mainImg)
currentFilter.setValue(beginImage, forKey: kCIInputImageKey)
// darken right-image by 40%
currentFilter.setValue(-0.4, forKey: kCIInputBrightnessKey)
if let output = currentFilter.outputImage {
if let cgimg = context.createCGImage(output, from: output.extent) {
let processedImage = UIImage(cgImage: cgimg)
// set the right-side image
rightImageView.image = processedImage
}
}
// darken bottom-image by 50%
currentFilter.setValue(-0.5, forKey: kCIInputBrightnessKey)
if let output = currentFilter.outputImage {
if let cgimg = context.createCGImage(output, from: output.extent) {
let processedImage = UIImage(cgImage: cgimg)
// set the bottom image
bottomImageView.image = processedImage
}
}
}
}
// skew the right-side and bottom image views
let skewVal: CGFloat = 1.0
// bottom part transform
let bottomTransform = CGAffineTransform(a: 1.0, b: 0.0, c: skewVal, d: 1.0, tx: 0.0, ty: 0.0)
bottomImageView.transform = bottomTransform
// right part transform
let rightTransform = CGAffineTransform(a: 1.0, b: skewVal, c: 0.0, d: 1.0, tx: 0.0, ty: 0.0)
rightImageView.transform = rightTransform
}
func resizeImage(image: UIImage, newSize: CGSize) -> UIImage {
let newWidth = newSize.width
let newHeight = newSize.height
UIGraphicsBeginImageContext(CGSize(width: newWidth, height: newHeight))
image.draw(in: CGRect(x: 0.0, y: 0.0, width: newWidth, height: newHeight))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
}
This is the result:
There is a variable named vDepth that controls the width of the right-side and the height of the bottom imageViews.
Note: this is just example code... Hopefully this will get you on your way.

Related

How to crop the shape of an image out of a caShape layer,

I have a cashape layer which looks like this
I have this P Image
I want the center of the triangle to be see-through in the same shape of P image (or any image) kind of like this
I have tried (among many many other things) this... which seems close, but it ONLY shows the P, I need it to show everything but the P
let viewTheTriangleIsIn = UIView()
let pImage = UIImageView()
let maskLayer = CAShapeLayer()
maskLayer.frame.origin = CGPoint(x: 0, y: 0)
maskLayer.frame.size = viewTheTriangleIsIn.bounds.size
maskLayer.contents = pImage.image!.cgImage
maskLayer.fillRule = .evenOdd
viewTheTriangleIsIn.layer.mask = maskLayer
Assuming your "P image (or any image)" has transparent (alpha) areas such as this (the P is surrounded by transparency):
so it would look like this in a UIImageView:
You can create an "inverted transparency" image:
guard let pImage = UIImage(named: "pImage") else {
print("Could not load mask image!")
return
}
// size of view you want to mask
let sz: CGSize = CGSize(width: 200, height: 200)
// rect to draw the mask image (where we want the "P")
let maskRect:CGRect = CGRect(x: 50, y: 100, width: 100, height: 80)
let renderer = UIGraphicsImageRenderer(size: sz)
let iMaskImage = renderer.image { ctx in
// fill with black (any color other than clear will do)
ctx.cgContext.setFillColor(UIColor.black.cgColor)
ctx.fill(CGRect(origin: .zero, size: sz))
// draw the image in maskRect with .xor blendMode
// this will make all non-transparent pixels in the image transparent
pImage.draw(in: maskRect, blendMode: .xor, alpha: 1.0)
}
At that point, iMaskImage will be this (the white "P" shape is not white... it's transparent):
You can then use that image as a mask on any other view.
Here's an example MaskedTriangleView -- it uses a CAShapeLayer for the triangle, and then uses the "P" image as a layer mask:
class MaskedTriangleView: UIView {
let shapeLayer = CAShapeLayer()
var maskImage: UIImage?
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
func commonInit() {
layer.addSublayer(shapeLayer)
shapeLayer.fillColor = UIColor.systemYellow.cgColor
shapeLayer.strokeColor = UIColor.blue.cgColor
shapeLayer.lineWidth = 1
}
override func layoutSubviews() {
super.layoutSubviews()
// create a triangle shape path
let bez: UIBezierPath = UIBezierPath()
bez.move(to: CGPoint(x: bounds.minX, y: bounds.maxY))
bez.addLine(to: CGPoint(x: bounds.midX, y: bounds.minY))
bez.addLine(to: CGPoint(x: bounds.maxX, y: bounds.maxY))
bez.close()
shapeLayer.path = bez.cgPath
if let img = maskImage {
// let's make our mask image
// 50% of the height of self
let h: CGFloat = bounds.height * 0.5
// 40% from the Top (leaving 10% space at the bottom)
let y: CGFloat = bounds.height * 0.4
// keep it proportionally sized
let ratio: CGFloat = h / img.size.height
// width is proportional to height
let w: CGFloat = img.size.width * ratio
// center horizontally
let x: CGFloat = (bounds.width - w) * 0.5
// rect to draw the mask image
let maskRect:CGRect = CGRect(x: x, y: y, width: w, height: h)
let renderer: UIGraphicsImageRenderer = UIGraphicsImageRenderer(size: bounds.size)
let iMaskImage: UIImage = renderer.image { ctx in
// fill with black (any color other than clear will do)
ctx.cgContext.setFillColor(UIColor.black.cgColor)
ctx.fill(CGRect(origin: .zero, size: bounds.size))
// draw the image in maskRect with .xor blendMode
// this will make all non-transparent pixels in the image transparent
img.draw(in: maskRect, blendMode: .xor, alpha: 1.0)
}
// create a layer
let maskLayer: CALayer = CALayer()
// set the new image as its contents
maskLayer.contents = iMaskImage.cgImage
// same frame as self
maskLayer.frame = bounds
// use it as the mask for the shape layer
shapeLayer.mask = maskLayer
}
}
}
and it will look like this (the top image is overlaid on an image view, the bottom image is added as a subview of the main view, with the green background showing through):
Here's the example controller:
class ImageMaskingVC: UIViewController {
var bkgImageView: UIImageView!
var triangleView1: MaskedTriangleView!
var triangleView2: MaskedTriangleView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemGreen
// make sure we can load the images
guard let bkimg = UIImage(named: "samplePic") else {
print("Could not load background image!")
return
}
guard let pImage = UIImage(named: "pImage") else {
print("Could not load mask image!")
return
}
// create the image view and set its image
bkgImageView = UIImageView()
bkgImageView.image = bkimg
// create a MaskedTriangleView and set its maskImage
triangleView1 = MaskedTriangleView()
triangleView1.maskImage = pImage
// create another MaskedTriangleView and set its maskImage
triangleView2 = MaskedTriangleView()
triangleView2.maskImage = pImage
// add the views
[bkgImageView, triangleView1, triangleView2].forEach {
if let v = $0 {
v.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(v)
}
}
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
// constrain background image view
// Top / Leading / Trailing at 20-pts
bkgImageView.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
bkgImageView.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
bkgImageView.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
// height proportional to background image size
bkgImageView.heightAnchor.constraint(equalTo: bkgImageView.widthAnchor, multiplier: bkimg.size.height / bkimg.size.width),
// constrain first MaskedTriangleView exactly on top of the background image view
triangleView1.topAnchor.constraint(equalTo: bkgImageView.topAnchor, constant: 20.0),
triangleView1.leadingAnchor.constraint(equalTo: bkgImageView.leadingAnchor, constant: 20.0),
triangleView1.trailingAnchor.constraint(equalTo: bkgImageView.trailingAnchor, constant: -20.0),
triangleView1.bottomAnchor.constraint(equalTo: bkgImageView.bottomAnchor, constant: -20.0),
// constrain the second MaskedTriangleView below the background image view
// with same width and height as the first MaskedTriangleView
triangleView2.topAnchor.constraint(equalTo: bkgImageView.bottomAnchor, constant: 20.0),
triangleView2.widthAnchor.constraint(equalTo: triangleView1.widthAnchor, constant: 0.0),
triangleView2.heightAnchor.constraint(equalTo: triangleView1.heightAnchor, constant: 0.0),
// centered horizontally
triangleView2.centerXAnchor.constraint(equalTo: triangleView1.centerXAnchor, constant: 0.0),
])
}
}

UIImage with the corner round itself

Due to the auto layout feature, I tried to use UIImage corner radius instead of UIImageView. The corner radius was too thin when the photo was too large, such as 4k x 4k, but when the photo was small, such as 500 x 500, the corner radius was too large. No matter what size the photo is, I want the corner radius to be 25. Do you have any suggestions?
I tried the following code from this, but it does not solve my problem.:
https://newbedev.com/how-to-set-corner-radius-to-uiimage-not-uiimageview-in-ios-swift
My goal is to have the corner radius equal to the size of any photo. I tested the image name "demo" which is 4,000 x 4,000, the corner radius is like 5, and "demo2" which is 500 x 500, the corner radius is like 50.
Here my full code:
class TheCountdownDetails: UIViewController {
let photoPreview = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
photoPreview.translatesAutoresizingMaskIntoConstraints = false
photoPreview.contentMode = .scaleAspectFit
view.addSubview(photoPreview)
photoPreview.topAnchor.constraint(equalTo: view.topAnchor, constant: 20).isActive = true
photoPreview.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20).isActive = true
photoPreview.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20).isActive = true
photoPreview.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20).isActive = true
}
override func viewDidLayoutSubviews() {
photoPreview.image = UIImage(named: "demo")?.withRoundedCorners(radius: 25)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
}
}
extension UIImage {
public func withRoundedCorners(radius: CGFloat? = nil) -> UIImage? {
let maxRadius = min(size.width, size.height) / 2
let cornerRadius: CGFloat
if let radius = radius, radius > 0 && radius <= maxRadius {
cornerRadius = radius
} else {
cornerRadius = maxRadius
}
UIGraphicsBeginImageContextWithOptions(size, false, scale)
let rect = CGRect(origin: .zero, size: size)
UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip()
draw(in: rect)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
If you use debug to inspect the UIImage returned from your withRoundedCorners(...) func, you'll see that both images do, in fact, have the same rounded corners.
The problem is that you are using a radius of 25 on a 4k x 4k image, and a radius of 25 on a 500 x 500 image, but then scaling them to fit your imageView.
If you change your imageView's content mode to:
photoPreview.contentMode = .topLeft
the images won't scale, and you'll see that you're getting the same radius rounded corners.
So, you need to scale the image at the same time you're clipping the rounded corners.
Here's a modification of your extension:
extension UIImage {
func withRoundedCorners(radius: CGFloat? = nil, targetSize: CGSize) -> UIImage {
// First, determine the scale factor that preserves aspect ratio
let widthRatio = targetSize.width / size.width
let heightRatio = targetSize.height / size.height
let scaleFactor = min(widthRatio, heightRatio)
// Compute the new image size that preserves aspect ratio
let scaledImageSize = CGSize(
width: size.width * scaleFactor,
height: size.height * scaleFactor
)
let maxRadius = min(scaledImageSize.width, scaledImageSize.height) / 2
let cornerRadius: CGFloat
if let radius = radius, radius > 0 && radius <= maxRadius {
cornerRadius = radius
} else {
cornerRadius = maxRadius
}
let newRect: CGRect = CGRect(origin: .zero, size: scaledImageSize)
let renderer = UIGraphicsImageRenderer(size: newRect.size)
let scaledImage = renderer.image { _ in
UIBezierPath(roundedRect: newRect, cornerRadius: cornerRadius).addClip()
self.draw(in: newRect)
}
return scaledImage
}
}
and an example controller, putting two imageViews in a stack view, so we can see two different size images at the same time:
class TheCountdownDetails: UIViewController {
let photoPreview1 = UIImageView()
let photoPreview2 = UIImageView()
override func viewDidLoad() {
super.viewDidLoad()
let stack = UIStackView()
stack.axis = .vertical
stack.distribution = .fillEqually
stack.spacing = 20
stack.translatesAutoresizingMaskIntoConstraints = false
stack.addArrangedSubview(photoPreview1)
stack.addArrangedSubview(photoPreview2)
view.addSubview(stack)
photoPreview1.contentMode = .center
photoPreview2.contentMode = .center
let g = view.safeAreaLayoutGuide
NSLayoutConstraint.activate([
stack.topAnchor.constraint(equalTo: g.topAnchor, constant: 20.0),
stack.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 20.0),
stack.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: -20.0),
stack.bottomAnchor.constraint(equalTo: g.bottomAnchor, constant: -20.0),
])
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// image views are in a stack view,
// so we need to force their layouts
// before asking for their frames
photoPreview1.setNeedsLayout()
photoPreview1.layoutIfNeeded()
photoPreview2.setNeedsLayout()
photoPreview2.layoutIfNeeded()
guard let img1 = UIImage(named: "image4kx4k") else { return }
guard let img2 = UIImage(named: "image500x500") else { return }
let img1r = img1.withRoundedCorners(radius: 25, targetSize: photoPreview1.frame.size)
let img2r = img2.withRoundedCorners(radius: 25, targetSize: photoPreview2.frame.size)
photoPreview1.image = img1r
photoPreview2.image = img2r
}
}
Using this 4kx4k image (original source: https://images.wallpaperscraft.com/image/single/night_city_aerial_view_city_lights_130879_4000x4000.jpg):
and this 500x500 image (original source: https://www.digitalphotopix.com/wp-content/uploads/2011/02/blue-lake.jpg)
We get this output:

How to get circle imageView in as titleView of navigation bar iOS

I am trying to get a navigation bar similar to the one in iOS messages app. This is what I have, and it creates a bit of a circle shape but gets cut off. If you were to recreate the centered image from Messages how would you?
let imageView = UIImageView(image: UIImage(named: "test-image"))
imageView.contentMode = .scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
imageView.frame = titleView.bounds
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = imageView.frame.height / 2
titleView.addSubview(imageView)
self.navigationItem.titleView = titleView
The current outcome with this snippet:
The desired outcome:
Tested solutions In dark mode:
Please excuse my eggs ;) It's my favorite test pic
Set image view content mode .scaleAspectFill
imageView.contentMode = .scaleAspectFill
The following is what I have done.
I use two functions to work with an image. The makeCutout function creates a square cutout based on the height of an image of your selection. The position of the cutout is placed at the center of the source image. (See let cutoutRect = ...) The second one, the makeCircularImage function, makes the cutout image circular. Note that I'm assuming the original image is horizontally-long, which means that the diameter of the cutout will be the height of the image.
import UIKit
class ViewController: UIViewController {
// MARK: - Life cycle
override func viewDidLoad() {
super.viewDidLoad()
if let image = UIImage(named: "jenniferGarner.jpg") {
let imageWidth = image.size.width
let imageHeight = image.size.height
let cutoutRect = CGRect(origin: CGPoint(x: (imageWidth - imageHeight) / 2.0, y: 0.0), size: CGSize(width: imageHeight, height: imageHeight))
if let cutoutImage = makeCutout(image: image, rect: cutoutRect) {
/* making it a circular image */
if let circleImage = makeCircularImage(sourceImage: cutoutImage, diameter: cutoutImage.size.height) {
testImageView.isHidden = true
let imageView = UIImageView(image: circleImage)
imageView.contentMode = .scaleAspectFit
let titleView = UIView(frame: CGRect(x: 0, y: 0, width: 44, height: 44))
//titleView.backgroundColor = UIColor.green
imageView.frame = titleView.bounds
imageView.layer.masksToBounds = true
imageView.layer.cornerRadius = imageView.frame.height / 2
titleView.addSubview(imageView)
self.navigationItem.titleView = titleView
}
}
}
}
// MARK: - Making a cutout image
func makeCutout(image: UIImage, rect: CGRect) -> UIImage? {
if let cgImage = image.cgImage {
let contextImage: UIImage = UIImage(cgImage: cgImage)
// Create bitmap image from context using the rect
var croppedContextImage: CGImage? = nil
if let contextImage = contextImage.cgImage {
if let croppedImage = contextImage.cropping(to: rect) {
croppedContextImage = croppedImage
}
}
// Create a new image based on the imageRef and rotate back to the original orientation
if let croppedImage: CGImage = croppedContextImage {
let image: UIImage = UIImage(cgImage: croppedImage, scale: image.scale, orientation: image.imageOrientation)
return image
}
}
return nil
}
func makeCircularImage(sourceImage: UIImage, diameter: CGFloat) -> UIImage? {
let imageView = UIImageView(image: sourceImage)
let layer = imageView.layer
layer.masksToBounds = true
layer.cornerRadius = diameter / 2.0
UIGraphicsBeginImageContext(imageView.bounds.size)
layer.render(in: UIGraphicsGetCurrentContext()!)
if let finalImage = UIGraphicsGetImageFromCurrentImageContext() {
UIGraphicsEndImageContext()
return finalImage
}
return nil
}
}
I have Jeniffer Garner as a guest as shown below. The source image size is 480 px X 180 px.
The following is the final work with an iPhone simulator with the dark mode.

Set UIView as SCNMaterial content

I'm trying to use a UIView with Arkit 3D space. I've seen a few examples which requires the UIView or UIView's layer is set as the diffuse content of the SCNMaterial object. That's not working as I've expected so far. I only see the frame of the view but the subviews within it are not added neither is a corner radius on the layer visible.
func setupBillBoard() {
let view = PlayerView(frame: CGRect(x: 0, y: 0, width: 70, height: 40))
view.backgroundColor = .red
view.layer.cornerRadius = 14
let material = SCNMaterial()
material.diffuse.contents = view.asImage()
material.isDoubleSided = true
let plane = SCNPlane(width: 1, height: 1)
plane.materials = [material]
let node = SCNNode()
node.geometry = plane
node.position = SCNVector3(box.x, box.y + 0.3, box.z - 0.4)
node.scale = SCNVector3(0.4, 0.4, 0.4)
self.billBoardNode = node
}
View Extension to convert UIView to image capture
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}
So far I get only a red square shaped view in the scene and nothing more
Custom class code here: https://www.paste.org/106551
Xib in attached image
Here's the way i set mines up and it works fine. You should be able to copy and paste the function inside viewDidLoad and you will see a red circle. You can also use an UIImageView instead of a UIView.
After you create you custom UIView call yourView.layoutIfNeeded() before adding it to the SCNMaterial.
You can also set the corner radius on a geometry: plane.cornerRadius = 0.015 like I did in createAndPositionGrayNode()
override func viewDidLoad() {
super.viewDidLoad()
createAndPositionRedCircleNode()
// or try it with an imageView, comment out the line above
// createAndPositionGrayNode()
}
func createAndPositionRedCircleNode() {
let redImage = UIColor.red.imageRepresentation
let myView = UIView()
myView.frame = CGRect(x: 0, y: 0, width: 250, height: 250)
myView.layer.contents = redImage.cgImage
myView.layoutIfNeeded() // <-- myView.layoutIfNeeded() called here
myView.layer.contentsGravity = CALayerContentsGravity.resizeAspectFill
myView.layer.contentsScale = UIScreen.main.scale
myView.layer.masksToBounds = true
myView.layer.cornerRadius = myView.frame.width / 2
let convertedImage = myView.asImage()
let material = SCNMaterial()
material.diffuse.contents = convertedImage
let plane = SCNPlane(width: 0.15, height: 0.15)
plane.materials = [material]
plane.firstMaterial?.isDoubleSided = true
let redNode = SCNNode(geometry: plane)
redNode.name = "redNode"
redNode.position = SCNVector3(0.0, 0.2, -0.7)
sceneView.scene.rootNode.addChildNode(redNode)
}
func createAndPositionGrayNode() {
let lightGrayImage = UIColor.lightGray.imageRepresentation
let imageView = UIImageView()
imageView.frame = CGRect(x: 0, y: 0, width: 250, height: 250)
imageView.image = lightGrayImage
imageView.contentMode = .scaleAspectFit
imageView.backgroundColor = .clear
imageView.layer.masksToBounds = true
let material = SCNMaterial()
material.diffuse.contents = imageView.image
let plane = SCNPlane(width: 0.4, height: 0.4)
plane.materials = [material]
plane.firstMaterial?.isDoubleSided = true
plane.cornerRadius = 0.015 // <-- geometry cornerRadius set here
let grayNode = SCNNode(geometry: plane)
grayNode.name = "grayNode"
grayNode.position = SCNVector3(0.0, 0.2, -0.7)
sceneView.scene.rootNode.addChildNode(grayNode)
}
extension UIView {
func asImage() -> UIImage {
let renderer = UIGraphicsImageRenderer(bounds: bounds)
return renderer.image { rendererContext in
layer.render(in: rendererContext.cgContext)
}
}
}

Can I resize just the image of an imageView?

So I have this code:
override func viewDidLoad() {
super.viewDidLoad()
let imageView = UIImageView()
view.addSubview(imageView)
imageView.translatesAutoresizingMaskIntoConstraints = false
imageView.layer.borderWidth = 5
imageView.layer.borderColor = UIColor.green.cgColor
NSLayoutConstraint.activate([
imageView.heightAnchor.constraint(equalToConstant: 200),
imageView.widthAnchor.constraint(equalToConstant: 200),
imageView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: 0),
imageView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 0),
])
let image = UIImage(named: "3")
//let image = UIImage(named: "3")?.resizeImageWith(newSize: CGSize(width: 5,height: 5))
imageView.image = image
}
That looks like this:
Im trying to resize just the image ( so like from the green border there is a margin) with this extension:
func resizeImageWith(newSize: CGSize) -> UIImage {
let horizontalRatio = newSize.width / size.width
let verticalRatio = newSize.height / size.height
let ratio = min(horizontalRatio, verticalRatio)
let newSize = CGSize(width: size.width * ratio, height: size.height * ratio)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0)
draw(in: CGRect(origin: CGPoint(x: 0, y: 0), size: newSize))
let newImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return newImage!
}
But when I use that like this:
let image = UIImage(named: "3")?.resizeImageWith(newSize: CGSize(width: 60,height: 60))
I get the following result:
And same with this:
let image = UIImage(named: "3")?.resizeImageWith(newSize: CGSize(width: 5,height: 5))
The image just get worst.
Is it possible to just resize the image in order to have a margin from the imageView? Thank you!
Change the image view content mode to Center:
imageView.contentMode = .center
I was able to produce what you wanted, but doing away with constraints. Using a container view that holds the imageView as a subview, you can use the container like a frame and set its border width and color.
With the frame in place, you can then resize the image as per your needs by just setting the frame of the imageView. The last 2 lines of the code are commented out, but if you uncomment them, you will see that the image will shrink to fit the new imageView frame size set to (50, 50).
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let containerView = UIView()
containerView.layer.borderWidth = 5
containerView.layer.borderColor = UIColor.green.cgColor
containerView.frame = CGRect(x: 0, y: 0, width: 200, height: 200)
containerView.center = view.center
let imageView = UIImageView()
view.addSubview(containerView)
containerView.addSubview(imageView)
let image = UIImage(named: "3")!
imageView.frame = CGRect(origin: imageView.frame.origin, size: CGSize(width: 200, height: 200))
imageView.image = image
imageView.contentMode = .scaleAspectFit
imageView.center = view.convert(containerView.center, to: containerView)
print("Image Width: \(image.size.width)")
print("Image Height: \(image.size.height)")
print("ImageView Width: \(imageView.frame.width)")
print("ImageView Height: \(imageView.frame.height)")
print("Container Width: \(containerView.frame.width)")
print("Container Height: \(containerView.frame.height)")
print("View Center: \(view.center.x),\(view.center.y)")
print("Container Center: \(containerView.center.x),\(containerView.center.y)")
//imageView.frame.size = CGSize(width: 50, height: 50)
//imageView.center = view.convert(containerView.center, to: containerView)
}
}
I used .scaleAspectFit so that the image will be forced to fit whatever size you set the imageView to, while maintaining its original aspect ratio. You can of course change this to suit your needs as well. If you need to set the size of your image in different ways, you'll need to change the imageView's contentMode property as appropriate. More info here:
https://developer.apple.com/documentation/uikit/uiimageview
The convert function just helps to layout the imageView where you want it, within its parent view.
The print debug statements are helpful for checking your layout, but of course, unnecessary.

Resources