How to insert a sublayer in swift?
You can use the following snippet
view.layer.insertSublayer(yourLayer, atIndex: yourIndex)
Adding a sublayer to a UIView (background view) of UITableViewCell covers other cell views?
Just use
backgroundRect.layer.insertSublayer(gradient, at: 0)
Instead of using
backgroundRect.layer.addSublayer(gradient)
Problem when inserting a sublayer below an existing layer in Swift?
You can try
self.mainView.insertSubview(focusBG, belowSubiew: tapView)
Or
self.mainView.layer.insertSublayer(focusBG.layer, below: tapView.layer) // not tested
Adding a sub layer or UIView as subview to the top using z-order of UIButton still goes behind the view or look transparent
As I understand you set borderWidth
and borderColor
for button.layer
.
This button.layer
hosts all other sublayer that you add to them that's why your border is always on the top.
So you can use additional view for drawing the circle or...
Just remove setting borderWidth
and borderColor
for button.layer
and add another sublayer that will cover your border.
For example:
func addBorder(borderWidth: CGFloat = 1.0, borderColor: UIColor = UIColor.blue) {
let layer = CALayer()
let frame = self.mybtn.frame
layer.frame = CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height)
layer.borderColor = borderColor.cgColor
layer.borderWidth = borderWidth
self.mybtn.layer.addSublayer(layer)
}
and in ViewDidLoad:
override func viewDidLoad() {
super.viewDidLoad()
addBorder()
addCircleBadge(20, color: UIColor.red)
}
Also one update for addCircleBadge
public func addCircleBadge(_ width: CGFloat, color: UIColor) {
...
self.mybtn.layer.insertSublayer(layer, at: 0)
}
Why adding sublayer overlaps subviews?
When you add your UILabel as a subview of your view it is adding your UILabel's layer as a sublayer of your view's layer. So when you add another sublayer to your view's layer it will be on top of your UILabel's layer.
You can either add your background layer before you add the UILabel or do:
Swift
view.layer.insertSublayer(backgroundLayer, below: yourLabel.layer)
Objective C
[view.layer insertSublayer:backgroundLayer below:yourLabel.layer]
and it should put the background behind the label.
add textview as a sublayer of an image ios swift
Just use UITextView
or UITextField
and add it as subview to the UIImageView
, something like:
let textView = UITextView()
//configure text view like you want
//add constraints or size it as you want
myImage.addSubview(textView)
Remember UIImageView
is just another subclass of UIView
so you can add subviews to it like for a regular UIView
.
Going on to what you are dealing with, since some of the views you add are CALayer
s and some will be UIView
s or subclasses of both (for instance UITextView
is UIView
subclass)
I would add two properties to your class:
var addedViews = [Any]()
var undoStack = [Any]()
I know this is not very Swift like since you can put anything into these two array
s but still.
Then when you create a new layer
or view
you also add it to addedViews
array:
let layer = CAShapeLayer()
layer.frame = CGRect(x: 30, y: 30, width: 100, height: 100)
layer.backgroundColor = UIColor.red.cgColor
layer.transform = CATransform3DMakeRotation(0.2, 0, 0, 1)
addedViews.append(layer)
So addedViews
array will hold references to all the views you added, so when you undo you can do just the following:
if let viewLayer = addedViews.last {
if let view = viewLayer as? UIView {
view.removeFromSuperview()
} else if let layer = viewLayer as? CALayer {
layer.removeFromSuperlayer()
}
undoStack.append(viewLayer)
addedViews.removeLast()
}
If you then want to redo the change you do the same thing but you get the last view from undoStack
like so:
if let viewLayer = undoStack.last {
if let view = viewLayer as? UIView {
self.view.addSubview(view)
} else if let layer = viewLayer as? CALayer {
view.layer.addSublayer(layer)
}
addedViews.append(viewLayer)
undoStack.removeLast()
}
Swift iOS - How to line up a sublayer position to match the center of button text
Here's a button that seems to look the way you want (of course you can adjust any parameters that don't suit your sensibilities):
This button is automatically red, corner-rounded, and considerably larger than its text (even when the button is positioned using auto layout).
Here's how it was achieved through a subclass:
class MyRedButton : UIButton {
override func layoutSubviews() {
super.layoutSubviews()
self.backgroundColor = .red
self.layer.cornerRadius = 10
}
override var intrinsicContentSize: CGSize {
var sz = super.intrinsicContentSize
sz.width += 30; sz.height += 30
return sz
}
}
Add a CALayer sublayer centered over UIImageView
The issue is that when viewDidLoad
is called the layout has not been done yet so your measurements are being taken from the initial state of the view controller , most likely from the storyboard size if thats what you are using.
You need to call blurFilterMask
once layout has completed and the final size of heroImageView
is known.
You can do this by overriding viewDidLayoutSubviews
which is ...
Called to notify the view controller that its view has just laid out its subviews.
Read the docs as there are some conditions but in your case this does work leaving you with this..
class ViewController: UIViewController {
@IBOutlet var heroImageView: UIImageView!
var blurFilterMask:BlurFilterMask! = nil
func resetMaskOverlay(){
if blurFilterMask == nil {
blurFilterMask = BlurFilterMask()
blurFilterMask.diameter = 120
heroImageView.layer.addSublayer(blurFilterMask)
}
blurFilterMask.frame = heroImageView.bounds
blurFilterMask.origin = heroImageView.center
}
override func viewDidLayoutSubviews() {
resetMaskOverlay()
}
}
I assumed this line in BlurFilterMask
CGContextTranslateCTM(ctx, 0.0, yDiff)*/
Was meant to be commented out as it didn't make much sense leaving...
class BlurFilterMask : CALayer {
let GRADIENT_WIDTH : CGFloat = 50.0
var origin : CGPoint = CGPointZero {
didSet {
setNeedsDisplay()
}
}
var diameter : CGFloat = 50.0 {
didSet {
setNeedsDisplay()
}
}
override func drawInContext(ctx: CGContext) {
CGContextSaveGState(ctx)
let clearRegionRadius : CGFloat = self.diameter * 0.5
let blurRegionRadius : CGFloat = clearRegionRadius + GRADIENT_WIDTH
let baseColorSpace = CGColorSpaceCreateDeviceRGB();
let colours : [CGFloat] = [0.0, 0.0, 0.0, 0.0, // Clear region
0.0, 0.0, 0.0, 0.6] // blur region color
let colourLocations : [CGFloat] = [0.0, 0.0]
let gradient = CGGradientCreateWithColorComponents (baseColorSpace, colours, colourLocations, 2)
CGContextDrawRadialGradient(ctx, gradient, self.origin, clearRegionRadius, self.origin, blurRegionRadius, .DrawsAfterEndLocation);
}
}
Can't access sublayer after adding a UIView into current UIView
Quick explanation:
When you do self.layer.sublayers as? [CAShapeLayer]
, you are supposing that ALL self.layer.sublayers
are of type CAShapeLayer
. But, are you sure about that, who told you this? There are other kind of CALayer
, and default components might come with some already. That's why it's failing. The idea, is with to use compactMap()
to keep only the CAShapeLayer
:
let sublayers = self.layer.sublayers.compactMap { $0 as? [CAShapeLayer] }
Detailed explanation:
if let sublayers = self.layer.sublayers as? [CAShapeLayer] {} else {}
The else
is called because self.layer.sublayers
is not cartable into, it's not a [CAShapeLayer]. In our case, they are layers, so it means that not all sublayer is a CAShapeLayer
.
So, let's keep only the CAShapeLayer
, and forget about the other one (CAGradientLayer
, CALayer
, etc.).
For that, we can use compactMap()
.
self.layer.sublayers.compactMap { aSublayer in
}
The closure logic is simple:
The array is iterated. On each iteration, the item is called aSublayer
. We do whatever we want, and we return a transformed value if needed, an Int, etc.
If we don't want to keep the transformed value, we return nil, and then it will be skipped.
Since we want to keep only the sublayers which are a CAShapeLayer
, we can use a simple cast on them:
if let aSubLayerAsShapeLayer = aSubLayer as? CAShapeLayer {
return aSubLayerAsShapeLayer
} else { //We won't keep it
return nil
}
Which is equivalent to
return aSubLayer as? CAShapeLayer
Then, with more simplification (Implicit Returns from Single-Expression Closures, Shorthand Argument Names
, more info on the doc), we get:
let sublayers = self.layer.sublayers.compactMap { $0 as? [CAShapeLayer] }
Also, since it's "sure" to have an array (might be empty), it's not an optional anymore, we remove the if let
into a let
.
Related Topics
Swift Firestore Check If Documents Exists
Is Gamescene.Sks Not Recommended for Game Building
Swift: Specialize Method of Generic Class for Function Types
Core Data with Pre-Filled .Sqlite (Swift3)
How to Call a Selector-Based Timer Method on a Swift Struct
Call Function When If Value in Nsuserdefaults.Standarduserdefaults() Changes
Can't Use In/Contains Operator with Collection'
Convert String to Staticstring
Background Upload with Share Extension
Ambigious Reference to Member Request() Issues with Alamofire After Migration to Swift 3
Xcode Playgrounds Shared Directory Not Working
Swift Repl: How to Save/Load the Repl State? (A.K.A. Suspend/Resume, Snapshot, Clone)
Is It Safe to Force Unwrap an Optional After Checking It Is Not Nil
How to Submit Swift 2.2 App with Xcode 7.3 When iOS 10 Is Released