custom UISlider subview frame is offset from main view - ios

my custom uislider background uiview is offset from original view frame.
and the further the the slider original (x,y) from 0,0 the more the offset.
Please check the image below.
uislider subview frame offset
import UIKit
class customSlier: UISlider {
override func awakeFromNib() {
super.awakeFromNib()
self.initializeSomeSettings()
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
private func initializeSomeSettings() {
let view: UIView = UIView()
view.frame = self.frame
view.backgroundColor = UIColor.yellow
self.insertSubview(view, belowSubview: self)
}
override func layoutSubviews() {
super.layoutSubviews()
}
}

I think the problem is at this line:
view.frame = self.frame
Let's say your scroller is at position (10, 10). Then the background frame is also at position (10,10) in the coordinator of the scroll frame, which is (20,20) in the original view. My suggestion is to replace this line by this:
view.frame = CGRect(origin: .zero, size: self.frame.size)
Hope this helps.

try this
view.frame = self.bounds

Related

iOS - CALayer protruding bounds on orientation change

I have a simple CustomView. I have a CAGradientLayer that I am adding to the layer.
#IBDesignable
class CustomView: UIView {
override init (frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
setupView()
}
private func setupView() {
let gradient = CAGradientLayer()
gradient.frame = bounds
gradient.colors = [UIColor.blue.cgColor, UIColor.yellow.cgColor]
layer.insertSublayer(gradient, at: 0)
}
}
Below is the Storyboard setup
Below is the result in portrait on iPad, which is fine.
When the device orientation is changed, the layer of the CustomView is protruded from its bounds.
When I checked the view hierarchy in Xcode, the frame of the CustomView is adjusted accordingly, but, it is the layer which is protruding. To cross check, I removed all codes in setupView and set only a background colour, on orientation change, CustomView is adjusted as expected. But, why does it happen for layers?
You are inserting a new layer each time when layoutSubviews method call. That means it shows the line of the first layer.
Just update the frame from layoutSubviews and call setup method from awakeFromNib.
class CustomView: UIView {
private let gradient = CAGradientLayer()
override init (frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func layoutSubviews() {
super.layoutSubviews()
gradient.frame = bounds
}
override func awakeFromNib() {
super.awakeFromNib()
setupView()
}
private func setupView() {
gradient.colors = [UIColor.blue.cgColor, UIColor.yellow.cgColor]
layer.insertSublayer(gradient, at: 0)
}
}

How to correctly resize PKDrawing when layout changes?

I'm trying to layer a PKCanvasView on top of an image, which works fine with the initial layout but when the view is resized (by rotating the iPad) the PKDrawing within is not scaled which results in the image annotations being in the incorrect place. I'm struggling to work out how to correctly scale the PKDrawing to preserve the correct positions.
This is an example demonstrating the issue:
class ImageDrawingView: UIView {
private let imageView = UIImageView(frame: .zero)
private let drawingView = PKCanvasView(frame: .zero)
private let toolPicker = PKToolPicker()
override init(frame: CGRect) {
super.init(frame: frame)
commonInit()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
commonInit()
}
private func commonInit() {
// Setup image view
addSubview(imageView)
imageView.image = UIImage(named: "TestImage") // Here I just used a screen grab of the lock screen
imageView.contentMode = .scaleAspectFill
// Setup drawing view
addSubview(drawingView)
drawingView.isOpaque = false
toolPicker.setVisible(true, forFirstResponder: drawingView)
toolPicker.addObserver(drawingView)
drawingView.becomeFirstResponder()
}
override func layoutSubviews() {
super.layoutSubviews()
imageView.frame = bounds
drawingView.frame = bounds
}
}
class ViewController: UIViewController {
private let imageDrawingView = ImageDrawingView(frame: .zero)
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageDrawingView)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
imageDrawingView.frame = view.bounds
}
}
I think if I can calculate the transform, I can then use drawingView.drawing.transform(using: transformScale). How do I go about calculating the transform please?

How to fit a thumb to custom size switch?

I'm working on the custom UISwitch. I have changed size using this:
self.transform = CGAffineTransform(scaleX: 1.25, y: 1.16).
And now I have one problem, the thumb is still default size.
How can I fit it with uiswitch?
class CustomSwitch:UISwitch {
override init(frame: CGRect) {
super.init(frame: frame)
self.viewDidLoad()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
self.viewDidLoad()
}
func viewDidLoad() {
self.transform = CGAffineTransform(scaleX: 1.25, y: 1.16)
self.setupAppearance()
self.setColors()
self.addTarget(self, action: #selector(toggleState), for: .valueChanged)
}
func setupAppearance() {
self.layer.borderColor = UIColor.HavelockBlue.cgColor
self.layer.borderWidth = 1.0
self.layer.cornerRadius = self.bounds.height / 2
}
func setColors() {
self.backgroundColor = .white
self.subviews.first?.subviews.first?.backgroundColor = .clear
self.onTintColor = .white
self.thumbTintColor = .HavelockBlue
}
#objc func toggleState() {
if self.isOn {
print("Dark mode is on")
} else {
print("Dark mode is off")
}
}
}
Your problem is, you settings constrained width and height for your custom UISwitch, and after then you are trying to transform this object, but what actually happen.
Inside this override init(frame: CGRect) and required init?(coder: NSCoder) methods if you using auto layout you don't have actually final size of your UIView, the size is taken from IB. But you are setting self.layer.cornerRadius = self.bounds.height / 2. If you will print values of frame.size and bounds.size you will see.
Simple solution is to remove constrained sizes from IB and just transform to your desire scale.
Example:
required init?(coder: NSCoder) {
super.init(coder: coder)
changeSwitchSize()
}
override init(frame: CGRect) {
super.init(frame: frame)
changeSwitchSize()
}
private func changeSwitchSize() {
print("Before transform switch frame size: \(frame.size), bounds size: \(bounds.size)")
self.transform = CGAffineTransform(scaleX: 1.25, y: 1.16)
print("After transform switch frame size: \(frame.size), bounds size: \(bounds.size)")
}
/// Before transform switch frame: (51.0, 31.0), (51.0, 31.0)
/// After transform switch frame: (63.75, 35.95999999999998), (51.0, 31.0)
But be aware than CGAffineTransform change view's frame relative to its superview
More about it: https://stackoverflow.com/a/11288488/6057764

custom header UIView to all the VC?

I am trying to create a custom header file to all the ViewControllers in project. The following image is an actual design.
So that I am trying to create Custom view with Xib file as below :
import UIKit
class HamburgrView: UIView {
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
}
override func layoutSubviews() {
}
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "HamburgrView", bundle: bundle)
// Assumes UIView is top level and only object in CustomView.xib file
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
}
We don't know how to draw that Hamburger menu icon with curve and its start position from SafeArealayoutGuide.
I trying one view with x position negative -40 and giving cornerRadius not coming perfect curve like above image.
Output look not good.
My ViewController Code :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let hamView = HamburgrView(frame: .zero)
self.view.addSubview(hamView)
hamView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
hamView.topAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.topAnchor, constant: 0),
hamView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0),
hamView.widthAnchor.constraint(equalToConstant: 120),
hamView.heightAnchor.constraint(equalToConstant: 80)
])
}
As #sandip answer I have changed like below :
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: self.center.y), radius: 50, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
self.layer.addSublayer(layer)
}
Output Looks Below :
Any idea would be appreciate.
Is this what you wanted?
You can easily achieve it with CAShapeLayer and UIBezierPath
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: self.view.center.y), radius: 30, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
view.layer.addSublayer(layer)
In your code change arc centre as x to be zero and y to hamburger view's vertical centre and add it to self.view
Hope it helps
EDIT 1:
As OP has asked for complete code in comment updating the answer
class HamburgrView: UIView {
override init(frame: CGRect) {
// 1. setup any properties here
// 2. call super.init(frame:)
super.init(frame: frame)
// 3. Setup view from .xib file
xibSetup()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
xibSetup()
}
override open func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
xibSetup()
}
override func layoutSubviews() {
}
func xibSetup() {
let tempView = loadViewFromNib()
// use bounds not frame or it'll be offset
tempView.frame = bounds
// Make the view stretch with containing view
tempView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
// Adding custom subview on top of our view (over any custom drawing > see note below)
addSubview(tempView)
}
func loadViewFromNib() -> UIView {
let bundle = Bundle(for: type(of:self))
let nib = UINib(nibName: "HamburgrView", bundle: bundle)
// Assumes UIView is top level and only object in CustomView.xib file
let view = nib.instantiate(withOwner: self, options: nil)[0] as! UIView
return view
}
override func didMoveToSuperview() {
super.didMoveToSuperview()
let layer = CAShapeLayer()
layer.path = UIBezierPath.init(arcCenter: CGPoint(x: 0, y: 40), radius: 50, startAngle: -1.5708, endAngle: 1.5708, clockwise: true).cgPath
layer.fillColor = UIColor.red.cgColor
self.layer.addSublayer(layer)
}
}
EDIT 2:
Reason OPs arc coming outside hamburger view is because of wrong arc centre
changing to CGPoint(x: 0, y: 40) from self.view.center.y should solve the issue. Updated the code to reflect the same
Why 40?
Because OP is setting height of hamburger view to 80. So 80/2 = 40
Why cant we use bounds.size.height / 2 or self.view.center.y?
because in didMoveToSuperview OP's constraint has not kicked in yet so it is taking the height view in xib which I don't think is 80 so it sets the arc centre with that value but then your constraint changes height to 80.
Here is final O/P:

Adding Blur effect to view

I have a view class which is used for showing a spinner with a blurred background view. I add this to another view during runtime and everything works fine. When I add this view to another ViewControllers view somehow the effect is not visible.The view does get added, I check it by setting the background colour to green and it is there.But the effect itself is not visible.I add the view in this manner
Ex:
self.view.addSubview(self.loadingView)
self.loadingView.frame = CGRect(origin: .zero, size:self.view.frame.size)
.The Implementation is as below,
final class LoaderView: UIView {
fileprivate let spinner = UIActivityIndicatorView(activityIndicatorStyle: .whiteLarge)
fileprivate let effect = UIBlurEffect(style: .light)
fileprivate let backgroundView = UIVisualEffectView(frame:.zero)
override init(frame: CGRect) {
spinner.startAnimating()
super.init(frame: frame)
backgroundView.effect = effect
addSubview(backgroundView)
addSubview(spinner)
}
#available(*, unavailable)
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setSpinnerColor(color:UIColor){
spinner.color = color
}
override func layoutSubviews() {
super.layoutSubviews()
backgroundView.frame = CGRect(x: 0, y: 0, width: self.bounds.height/5, height: self.bounds.height/5)
backgroundView.center = CGPoint(x:self.bounds.size.width / 2.0, y: self.bounds.size.height / 2.0)
backgroundView.clipsToBounds = true
backgroundView.cornerRadius = 10
spinner.center = center
}
}

Resources