Just as the title says. I have a UIImageView and I am inserting CAShapeLayers into the View. The issue is that I don't know how to insert them behind the image of the UIImageView. Any advice would be much appreciated, thanks.
You could try to do some layer re-ordering, but you might be foiled by UIImageView's own internal code. My suggestion would be be to create a container view and add your sublayers there, then add the image view as a subview (which is essentially adding it as a sublayer as well). As long as the image has transparency and the UIImageView is not opaque, it will work.
This code worked for me in a playground:
let container = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let imageView = UIImageView(frame: container.bounds)
imageView.image = UIImage(named: "duck.png")
imageView.isOpaque = false
let shape = CAShapeLayer()
shape.path = UIBezierPath(roundedRect: container.bounds, cornerRadius: 2).cgPath
shape.strokeColor = UIColor.black.cgColor
shape.lineWidth = 10
shape.fillColor = UIColor.blue.cgColor
shape.borderColor = UIColor.black.cgColor
container.layer.addSublayer(shape)
container.addSubview(imageView)
Good luck!
Related
I have a tab bar that I'm trying to customize it's look. So far, I changed it's background color and added corner radius. I'm trying to add a shadow to it, but I can't get it to work.
I tried few solutions, but nothing worked for me. There is not errors or anything, but it just won't work. This is what I have so far:
private func setupTabBarAppearance() {
let CORNER_RADIUS: CGFloat = 20
let tabBar = self.tabBar
tabBar.barTintColor = UIColor.appWhite
tabBar.unselectedItemTintColor = .lightGray
tabBar.tintColor = UIColor.appBlue
tabBar.layer.masksToBounds = true
tabBar.layer.cornerRadius = CORNER_RADIUS
tabBar.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
tabBar.layer.shouldRasterize = true
let shapeLayer = CAShapeLayer()
let path = UIBezierPath(roundedRect: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: tabBar.frame.height), byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: CORNER_RADIUS, height: CORNER_RADIUS)).cgPath
shapeLayer.path = path
shapeLayer.shadowColor = UIColor.black.cgColor
shapeLayer.shadowOffset = CGSize(width: 2, height: 5)
shapeLayer.shadowOpacity = 1
shapeLayer.shadowRadius = 10
shapeLayer.shouldRasterize = true
shapeLayer.rasterizationScale = UIScreen.main.scale
tabBar.layer.rasterizationScale = UIScreen.main.scale
tabBar.layer.addSublayer(shapeLayer)
}
I have two problems with this that I don't find a solution to, which is:
I can't get the shapeLayer to be behind tabBar, it just covers it.
The shadow from shapeLayer is facing downwards so even if I would be able to get the shapeLayer to be behind the tab bar, the shadow still won't be displayed.
Let’s just tackle this part:
tabBar.layer.addSublayer(shapeLayer)
Obviously if you add a shape layer to the tab bar’s own layer, the shape layer must be in front. That is what it means to be a sublayer.
If you want to cast a shadow using a custom shape, you will need a completely separate view hidden behind your tab bar.
I wanted to recreate the AppStore's "today" cards with rounded corners and a light drop shadow.
I created a path, a maskLayer and a separate shadowLayer, which – according to several sources – is the way of doing it.
The problem, however, is that my lovely rounded rectangle with a shadow has got some gray strokes at it corners. How can I solve this? I tried different shadow opacities and different radii. It didn't solve my problem.
Here you can see my screenshots and my code below.
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
// create sample view and add to view hierarchy
let bigTeaser = UIView(frame: CGRect(x: 16, y: 200, width: 343, height: 267))
bigTeaser.backgroundColor = UIColor.white
view.addSubview(bigTeaser)
// create the path for the rounded corners and the shadow
let roundPath = UIBezierPath(roundedRect: bigTeaser.bounds, cornerRadius: 20)
// create maskLayer
let maskLayer = CAShapeLayer()
maskLayer.frame = bigTeaser.bounds
maskLayer.path = roundPath.cgPath
bigTeaser.layer.mask = maskLayer
// create shadowLayer
let shadowLayer = CAShapeLayer()
shadowLayer.path = roundPath.cgPath
shadowLayer.frame = bigTeaser.frame
shadowLayer.shadowOpacity = 0.3
shadowLayer.shadowRadius = 24
shadowLayer.shadowColor = UIColor.black.cgColor
shadowLayer.shadowOffset = CGSize(width: 0, height: 2)
// insert layers
bigTeaser.superview!.layer.insertSublayer(shadowLayer, below: bigTeaser.layer)
}
If you replace:
shadowLayer.path = roundPath.cgPath
to
shadowLayer.shadowPath = roundPath.cgPath
The ugly borders will magically disappear.
I am trying to add a UIImage to the UICollectionViewController by using the following code:
let backgroundImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 430, height: 550))
backgroundImage.image = UIImage(named: "fantasy_football_hero_tile_cropped.png")
backgroundImage.contentMode = UIView.ContentMode.scaleAspectFit
backgroundImage.clipsToBounds = true
self.view.insertSubview(backgroundImage, at: 0)
My issue is that the image is not at the bottom/lowest part of the screen which is what I am trying to achieve... how can I clip it to the bottom?! I am not using story board, can I programmatically anchor the image to the bottom as I could do in a storyboard?
This is especially true since the screen sizes for the phones are different, but the desire is still to have it anchored to the bottom.
you can add programatically. I think you add/insert some views to the self.view that will also affect simple scenario
let backgroundImageV = UIView(frame: CGRect(x: 0, y: 0, width: 430, height: 500))
backgroundImageV.backgroundColor = UIColor.blue
backgroundImageV.contentMode = UIView.ContentMode.scaleAspectFit
backgroundImageV.clipsToBounds = true
self.view.addSubview(backgroundImageV)
let backgroundImageV2 = UIView(frame: CGRect(x: 0, y: 0, width: 430, height: 510))
backgroundImageV2.backgroundColor = UIColor.green
backgroundImageV2.contentMode = UIView.ContentMode.scaleAspectFit
backgroundImageV2.clipsToBounds = true
self.view.insertSubview(backgroundImageV2, at: 0)
let backgroundImage = UIImageView(frame: CGRect(x: 0, y: 0, width: 430, height: 550))
backgroundImage.backgroundColor = UIColor.red
backgroundImage.contentMode = UIView.ContentMode.scaleAspectFit
backgroundImage.clipsToBounds = true
self.view.insertSubview(backgroundImage, at: 0)
Here Red will be bottom Green will be in 1. because you add that red colored view later. So It will work. Can you please show your full code. It will work
One problem here is that self.view for a UICollectionViewController is the UICollectionView. This is a scroll view, so no matter where you put this image view, it is going to move when the scroll view scrolls.
If that isn't what you want, there is a simple solution: set the image view as the collection view's backgroundView. That is a stationary view that forms the background to the collection view no matter how it is scrolled. If you set the image view's contentMode to bottom the image will be centered at the bottom, which seems to be what you are after.
I want to add a logo as a background image to my tableView. Image size is 50px, 50px.
I tried the code below, but this puts the image lower right corner.
let imageView = UIImageView(image: UIImage(named: "logo"))
imageView.contentMode = .scaleAspectFit
imageView.layer.frame = CGRect(x: self.view.frame.midX, y: self.view.frame.midY, width: 50, height: 50)
let tableViewBackgroundView = UIView()
tableViewBackgroundView.addSubview(imageView)
self.tableView.backgroundView = tableViewBackgroundView
There are a few points about swift that are pretty key:
1) The x and y parameters in CGRect.init(x:y:width:height:) don't refer to the center of the imageView. Instead, they are points in a coordinate system where (0, 0) is the Upper Left Corner of the view that the CGRect is being presented over, and
2) UIImageView actually inherits from UIView, so because of this you can just say
tableView.backgroundView = imageView
You shouldn't have to fiddle around with any CGRects, I believe this code should work just fine, although you may have to fiddle with different contentMode properties to get it to display how you like.
let imageView = UIImageView(image: UIImage(named: "logo"))
imageView.layer.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
imageView.layer.frame.midX = tableView.layer.frame.midX
imageView.layer.frame.midY = tableView.layer.frame.midY
tableView.backgroundView = imageView
This is what I'm getting with this code
private func setupBorders(){
let path = UIBezierPath(roundedRect: mainTableView.bounds, byRoundingCorners: [.topLeft, .topRight], cornerRadii: CGSize(width: 20, height: 20))
let maskLayer = CAShapeLayer()
maskLayer.path = path.cgPath
mainTableView.layer.mask = maskLayer
mainTableView.layer.borderColor = UIColor.darkGray.cgColor
mainTableView.layer.borderWidth = 1.0
}
MainTableView is a uiview containing the notepad table and the table header. If I can get it to work for any UIView then it will work for this one. Much appreciation to anyone who can help!
Edit: In case its not clear, the problem is the border disappears on the rounded corners.
A mask layer is not enough to solve your requirement, because the layer border will not respect to the layer mask. Instead you should create a view for drawing the backgound and the border, and it should clip its contents along the border, too.
In storyboard drag a UIView to your ViewController, set constrains as you want, link it to NewView and try this,
import UIKit
import QuartzCore
class ViewController: UIViewController{
#IBOutlet var NewView :UIView!
override func viewDidLoad() {
super.viewDidLoad()
let screenSize: CGRect = UIScreen.mainScreen().bounds
let HeightFloat :CGFloat = screenSize.height - 60
let WidthFloat :CGFloat = screenSize.width - 50
let NewRect :CGRect = CGRectMake(10, 20, WidthFloat, HeightFloat)
let maskPath = UIBezierPath(roundedRect: NewRect,
byRoundingCorners: [.TopLeft, .TopRight],
cornerRadii: CGSize(width: 10, height: 10))
let shape = CAShapeLayer()
shape.path = maskPath.CGPath
shape.fillColor = UIColor.clearColor().CGColor
shape.strokeColor = UIColor.darkGrayColor().CGColor
shape.lineWidth = 2
shape.lineJoin = kCALineJoinRound
NewView.layer.mask = shape
}
}
You will get your output,