UISegmentedControl Corner Radius Not Changing - ios

UISegmentedControl corner radius is not changing. I also followed some answers in this question, my UISegmentedControl's corner radius still is not changing. I followed This tutorial to create UISegmentedControl.
Code:
import UIKit
class SegmentViewController: UIViewController {
private let items = ["Black", "Red", "Green"]
lazy var segmentedConrol: UISegmentedControl = {
let control = UISegmentedControl(items: items)
return control
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
setupViews()
}
fileprivate func setupViews(){
view.addSubview(segmentedConrol)
segmentedConrol.translatesAutoresizingMaskIntoConstraints = false //set this for Auto Layout to work!
segmentedConrol.heightAnchor.constraint(equalToConstant: 40).isActive = true
segmentedConrol.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 40).isActive = true
segmentedConrol.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -40).isActive = true
segmentedConrol.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
segmentedConrol.selectedSegmentIndex = 1
//style
segmentedConrol.layer.cornerRadius = 20
segmentedConrol.layer.borderWidth = 2
segmentedConrol.layer.borderColor = UIColor.black.cgColor
segmentedConrol.backgroundColor = .red
segmentedConrol.selectedSegmentTintColor = .darkGray
// segmentedConrol.clipsToBounds = true
segmentedConrol.layer.masksToBounds = true
}
}
(PS. Probably the answer is so simple for most people, please do not mind me, I am new in this field.)

Subclass UISegmentedControl and override layoutSubviews. Inside the method set the corner radius to what you want it to be, and you can remove the portion where you set the corner radius in setupViews():
class YourSegmentedControl: UISegmentedControl {
override func layoutSubviews() {
super.layoutSubviews()
layer.cornerRadius = 20
}
}
In your view controller where you create segmentedControl create an instance of YourSegmentedControl like below.
lazy var segmentedConrol: YourSegmentedControl = {
let control = YourSegmentedControl(items: items)
return control
}()
The result is:

Related

How to fetch the width and height of a programatically added view in Swift/ iOS?

I have added a UIView and a UIImageView programatically, but I need its width and height.
Is it possible to fetch it, if yes, please share how to fetch it.
Code:
let View1: UIView = {
let viewView = UIView()
viewView.translatesAutoresizingMaskIntoConstraints = false
viewView.contentMode = .scaleAspectFit
print(UserDefaults.standard.bool(forKey: "backgroundColourSelected"))
if UserDefaults.standard.bool(forKey: "backgroundColourSelected") {
viewView.backgroundColor = self.viewColor
}else {
viewView.backgroundColor = .white
}
viewView.clipsToBounds = true
return viewView
}()
self.canvasView.addSubview(View1)
View1.centerXAnchor.constraint(equalTo: canvasView.centerXAnchor, constant: 0).isActive = true
View1.centerYAnchor.constraint(equalTo: canvasView.centerYAnchor, constant: 0).isActive = true
View1.widthAnchor.constraint(equalTo: canvasView.widthAnchor, constant: 0).isActive = true
View1.heightAnchor.constraint(equalTo: canvasView.widthAnchor, multiplier: aspectRatio).isActive = true
You need to know the life cycle of view controller.
To get height of any view use viewDidLayoutSubviews.
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
print(view1.frame.size)
}
size property will print both final height and width.
use override func viewWillLayoutSubviews() for ViewController and override func layoutSubviews() for UIView.
Example : - for UIViewControllers
override func viewWillLayoutSubviews() {
print(yourView.frame.height)
}
for UIViews
override func layoutSubviews() {
print(yourView.frame.height)
print(yourView.frame.width)
}
Since your view is inside the function just move it out
class YourController / YourView {
private let imgView: UIImageView = {
let iv = UIImageView()
// do whatever you want with your image using iv. eg. iv.contentMode = .scaleAspectFit or iv.image = UIImage(named: "custom_img")
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imgView)
//set constraints for your
}
// then use layoutSubViews(for views) / viewWillLayoutSubViews (for controller) and calculate the height
}

How to create a circular UIImageView

I am having issue creating a circular UIImageView. If I were to manually set the corderRadius to a value, eg. 50, it will have rounded corner. But when I try to set it as half of the frame's height or width (frame.width / 2 or frame.height / 2), it doesn't work. Somehow, the frame is (0, 0, 0, 0) when I try to print it.
And here is my code,
import UIKit
class TestIconController : UIViewController {
let icon: UIImageView = {
let imageView = UIImageView()
imageView.layer.cornerRadius = imageView.frame.width / 2
imageView.clipsToBounds = true
imageView.layer.masksToBounds = true
imageView.backgroundColor = .red
imageView.translatesAutoresizingMaskIntoConstraints = false
return imageView
}()
override func viewDidLoad() {
super.viewDidLoad()
loadLogo()
}
func loadLogo() {
view.addSubview(icon)
// Constraints
icon.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
icon.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
icon.heightAnchor.constraint(equalToConstant: 200).isActive = true
icon.widthAnchor.constraint(equalToConstant: 200).isActive = true
}
}
Override this function.
override func viewDidLayoutSubviews() {
icon.layer.cornerRadius = icon.bounds.size.width / 2
icon.clipsToBounds = true
icon.layer.masksToBounds = true
}
You may also make a base class for it for batter handling; Like
class UICirlceImageView : UIImageView {
override open func layoutSubviews() {
super.layoutSubviews();
let layer:CALayer = self.layer;
layer.cornerRadius = self.frame.size.width/2.0;
layer.masksToBounds = true;
}
}
then, do it like this
//let icon: UICirlceImageView = { // You may initialize like this as well
let icon: UIImageView = {
let imageView = UICirlceImageView()
imageView.backgroundColor = .red
//imageView.translatesAutoresizingMaskIntoConstraints = false // Don't know if it is needed
return imageView
}()
Note: The answer given by Rushabh is also correct.
You can create this extensions
extension UIImageView {
func setRounded() {
self.layer.cornerRadius = self.frame.width / 2
self.layer.masksToBounds = true
self.clipsToBounds = true
}
}
In your case you can inside viewDidLayoutSubviews call
icon.setRounded()

Why does UIView fill the superView?

I am creating a subview programmatically that I would like to be positioned over a superView, but I do not want it to fill the enter superView.
I have been checking around to see if this question has been asked before but for some reason, I can only find answers to how to fill the entire view.
I would really appreciate it if someone could help critique my code and explain how to position a subView instead of filling the entire superview.
class JobViewController: UIViewController {
var subView : SubView { return self.view as! SubView }
var requested = false
let imageView: UIImageView = {
let iv = UIImageView(image: #imageLiteral(resourceName: "yo"))
iv.contentMode = .scaleAspectFill
iv.isUserInteractionEnabled = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(imageView)
imageView.fillSuperview()
subView.requestAction = { [ weak self ] in
guard let strongSelf = self else { return }
strongSelf.requested = !strongSelf.requested
if strongSelf.requested {
UIView.animate(withDuration: 0.5, animations: {
strongSelf.subView.Request.setTitle("Requested", for: .normal)
strongSelf.subView.contentView.backgroundColor = UIColor.red.withAlphaComponent(0.5)
})
} else {
UIView.animate(withDuration: 0.5, animations: {
strongSelf.subView.Request.setTitle("Requested", for: .normal)
strongSelf.subView.contentView.backgroundColor = UIColor.blue
})
}
}
}
override func loadView() {
// I know the issue lies here, but how would I go about setting the frame of the subview to just be positioned on top of the mainView?
self.view = SubView(frame: UIScreen.main.bounds)
}
}
I have my subView built in a separate file, I am not sure whether or not I would need its information since It is just what is inside of the subview.
You should add your subView as a subview of self.view and not set it equal your main view. And then set the constraints accordingly.
override func viewDidLoad() {
self.view.addSubview(subView)
subview.translatesAutoresizingMaskIntoConstraint = false
addSubview.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 0).isActive = true
addSubview.trailingAnchor.constraint(equalTo: self.view.trailingAnchor, constant: 0).isActive = true
addSubview.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 0).isActive = true
addSubview.bottomAnchor.constraint(equalTo: self.view.bottomAnchor, constant: 0).isActive = true
}
Regarding your initialisation problem try:
var subView = SubView()
I hope I understood your question correct.

Mask UIView with another UIView

Yes this question has been asked before, the solutions did not work or had different applications.
It is the most basic setup. I have two rectangular UIViews, red and blue.
I would like the blue square to cut into the red square, so the red square looks like an "L"
import Foundation
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
// Things which don't work
//viewA.mask = maskView // both views disappear
//viewA.layer.mask = maskView.layer // both views disappear
//viewA.layer.addSublayer(maskView.layer) // hides mask view
}
var viewA: UIView = {
let view = UIView()
view.backgroundColor = .red
view.layer.masksToBounds = true
return view
}()
var maskView: UIView = {
let view = UIView()
view.backgroundColor = .blue
return view
}()
}
This is the result I am expecting: (done in Photoshop)
As there is no magic way to mask the way in iOS, I present here a simple way to achieve this.
Don't forget to pan the clear area, If leaving the red square, it will become a blue square.
It's not hard to modify the subclass of UIViews for your own purpose, especially views.
import UIKit
class TestController: UIViewController {
override func viewDidLoad() {
view.backgroundColor = .gray
view.addSubview(viewA)
view.addSubview(maskView)
maskView.maskedView = viewA
viewA.activeMask = maskView
viewA.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
viewA.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
viewA.widthAnchor.constraint(equalToConstant: 100).isActive = true
viewA.heightAnchor.constraint(equalToConstant: 100).isActive = true
viewA.translatesAutoresizingMaskIntoConstraints = false
maskView.centerXAnchor.constraint(equalTo: view.centerXAnchor, constant: 50).isActive = true
maskView.centerYAnchor.constraint(equalTo: view.centerYAnchor, constant: -50).isActive = true
maskView.widthAnchor.constraint(equalToConstant: 100).isActive = true
maskView.heightAnchor.constraint(equalToConstant: 100).isActive = true
maskView.translatesAutoresizingMaskIntoConstraints = false
}
var viewA: MyUIView = {
let view = MyUIView()
view.backgroundColor = .clear
view.layer.masksToBounds = true
return view
}()
var maskView: ActiveMaskView = {
let view = ActiveMaskView()
view.backgroundColor = .clear
return view
}()
}
class ActiveMaskView: UIView{
override func didMoveToSuperview() {
super.didMoveToSuperview()
let panGesture = UIPanGestureRecognizer.init(target: self, action: #selector(moveAround(_:)))
self.addGestureRecognizer(panGesture)
}
weak var maskedView : UIView?
private var frameOrigin : CGPoint = CGPoint.zero
#objc func moveAround(_ panGesture: UIPanGestureRecognizer){
guard let superview = superview else {return}
switch panGesture.state {
case .began:
frameOrigin = frame.origin
self.backgroundColor = UIColor.blue
case .changed:
let translation = panGesture.translation(in: superview)
frame = CGRect.init(origin: CGPoint.init(x: frameOrigin.x + translation.x, y: frameOrigin.y + translation.y), size: frame.size)
maskedView?.setNeedsDisplay()
break
case .ended:
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
case .cancelled:
frame = CGRect.init(origin: frameOrigin , size: frame.size)
self.backgroundColor =
frame.intersects(maskedView!.frame) ?
UIColor.clear : UIColor.blue
maskedView?.setNeedsDisplay()
default:
break;
}
}
}
class MyUIView: UIView{
weak var activeMask: ActiveMaskView?
override func draw(_ rect: CGRect) {
super.draw(rect)
let ctx = UIGraphicsGetCurrentContext()
ctx?.setFillColor(UIColor.red.cgColor)
ctx?.fill(self.layer.bounds)
ctx?.setBlendMode(.sourceOut)
guard let activeMask = activeMask , let superview = superview else {
return
}
let sc = frame.intersection(activeMask.frame)
let interSection = superview.convert(sc, to: self)
ctx?.fill(interSection )
}
}

call label postion and size from outside of class (Swift4)

What I am trying to do is assign the position and size of a label from outside a class. Then within 2 separate classes call the label to add text to it. This would save time a lot of time if this would work.
let backbutton = UILabel!
backbutton.translatesAutoresizingMaskIntoConstraints = false
backbutton.leftAnchor.constraint(equalTo: _, constant: 20).isActive = true
backbutton.topAnchor.constraint(equalTo: _, constant: 125).isActive = true
backbutton.widthAnchor.constraint(equalToConstant: 50).isActive = true
backbutton.heightAnchor.constraint(equalToConstant: 50).isActive = true
class nineViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
backbutton.text = String("red")
}
}
class two: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
backbutton.text = String("two")
}
}
Create a Utilities class separately to use the functions that are inside it globally.
Utilities:
class Utilities: NSObject
{
class func createLabel(on view: UIView, horizontalAnchors hAnchors: (leading: CGFloat, leadingView: UIView, trailing: CGFloat, trailingView: UIView), verticalAnchors vAnchors: (top: CGFloat, topView: UIView, bottom: CGFloat, bottomView: UIView)) -> UILabel {
let label = UILabel()
view.addSubview(label)
label.backgroundColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: hAnchors.leadingView.leadingAnchor, constant: hAnchors.leading).isActive = true
label.trailingAnchor.constraint(equalTo: hAnchors.trailingView.trailingAnchor, constant: -hAnchors.trailing).isActive = true
label.topAnchor.constraint(equalTo: vAnchors.topView.topAnchor, constant: vAnchors.top).isActive = true
label.bottomAnchor.constraint(equalTo: vAnchors.bottomView.topAnchor, constant: -vAnchors.bottom).isActive = true
return label
}
class func createLabel(on view: UIView, positionAnchors pAnchors: (leading: CGFloat, leadingView: UIView, top: CGFloat, topView: UIView), size: (width: CGFloat, height: CGFloat)) -> UILabel {
let label = UILabel()
view.addSubview(label)
label.backgroundColor = UIColor.red
label.translatesAutoresizingMaskIntoConstraints = false
label.leadingAnchor.constraint(equalTo: pAnchors.leadingView.leadingAnchor, constant: pAnchors.leading).isActive = true
label.topAnchor.constraint(equalTo: pAnchors.topView.topAnchor, constant: pAnchors.top).isActive = true
label.widthAnchor.constraint(equalToConstant: size.width).isActive = true
label.heightAnchor.constraint(equalToConstant: size.height).isActive = true
return label
}
}
In ViewController:
#IBOutlet weak var autoLayedoutLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
let originY: CGFloat = 50
let spacing: CGFloat = 16
let width: CGFloat = 300
let height: CGFloat = 50
let label = Utilities.createLabel(on: view, positionAnchors: (spacing, view, originY, view), size: (width, height))
label.text = "Label with Position Anchors & Size"
label.backgroundColor = UIColor.red
let label2 = Utilities.createLabel(on: view, horizontalAnchors: (spacing, view, spacing, view), verticalAnchors: (spacing + height, label, spacing, autoLayedoutLabel))
label2.text = "Label with Horizontal & Vertical Anchors"
label2.backgroundColor = UIColor.green
}
You can have different variable for buttonText and set his position and size in his setter like
var buttonText:String {
didSet{
backButton.text = buttonText
setFontAndPosition()
}
}
and in viewController just set the value
override func viewDidLoad() {
super.viewDidLoad()
buttonText = "red"
}
I found it's feasible to directly use global UILable. If you don't need to manage too many labels, this is the simplest way.
A TabBarcontroller is used for testing here.
let backbutton = UILabel()
class MyTabBarController : UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
setViewControllers([SettingViewController(), NineViewController(), TwoViewController()], animated: false)
}
}
class SettingViewController: UIViewController {
override var tabBarItem: UITabBarItem!{
get {
return UITabBarItem.init(title: "setting", image: nil, tag: 0)
}
set{
super.tabBarItem = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.view.addSubview(backbutton)
backbutton.text = "cool"
backbutton.translatesAutoresizingMaskIntoConstraints = false
backbutton.leftAnchor.constraint(equalTo: self.view.leftAnchor, constant: 20).isActive = true
backbutton.topAnchor.constraint(equalTo: self.view.topAnchor, constant: 125).isActive = true
backbutton.widthAnchor.constraint(equalToConstant: 50).isActive = true
backbutton.heightAnchor.constraint(equalToConstant: 50).isActive = true
}
}
class NineViewController: UIViewController {
override var tabBarItem: UITabBarItem!{
get {
return UITabBarItem.init(title: "nine", image: nil, tag: 0)
}
set{
super.tabBarItem = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
backbutton.text = String("red")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
backbutton.text = String("red-Appear")
}
}
class TwoViewController: UIViewController {
override var tabBarItem: UITabBarItem!{
get {
return UITabBarItem.init(title: "two", image: nil, tag: 0)
}
set{
super.tabBarItem = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
backbutton.text = String("two")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
backbutton.text = String("two-Appear")
}
}
If you prefer defining the label inside one class. You may define the global UILabel as this:
weak var backbutton: UILabel!
class SettingViewController: UIViewController {
let mybutton = UILabel()
backbutton = mybutton
// continue
}
You don't need to change any other codes.
Now is the second part of the story. If you wanna setup a global UILabel outside any view, is that possible. Without constraints it's very simple like this:
let backbutton: UILabel! = {
let button = UILabel()
button.text = "test"
button.frame = CGRect.init(x: 200, y: 200, width: 50, height: 50)
return button
}()
The setting View changes like this :
class SettingViewController: UIViewController {
override var tabBarItem: UITabBarItem!{
get {
return UITabBarItem.init(title: "setting", image: nil, tag: 0)
}
set{
super.tabBarItem = newValue
}
}
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
self.view.addSubview(backbutton)
}
}
It's clear there is only one line in the SettingVC. But if you need to use constraints, what should we do? Everything else is fine, but the position of UILabel constraints depends on the superView of UILabel. So an extension can be used here to make things easier.
let specialLabelTag = 1001
let backbutton: UILabel! = {
let button = UILabel()
button.tag = specialLabelTag
button.text = "test" // for test purpose
button.translatesAutoresizingMaskIntoConstraints = false
button.widthAnchor.constraint(equalToConstant: 50).isActive = true
button.heightAnchor.constraint(equalToConstant: 50).isActive = true
return button
}()
extension UILabel{
override open func didMoveToSuperview() {
superview?.didMoveToSuperview()
if(tag == specialLabelTag){
leftAnchor.constraint(equalTo: superview!.leftAnchor, constant: 20).isActive = true
topAnchor.constraint(equalTo: superview!.topAnchor, constant: 125).isActive = true
}
}
The tag used in extension is to identify the global UILabel in order not to affect other UILabels. Only position constraints are needed in the extension. SettingUP vc is as same as before.
Now you can build a label without any view class. But you have to add them somewhere and modify the text as you like. Hope this is the answer to the question.
BTW, you can subclass the UILabel to MyUILabel with above code and then make it global (just put outside any class). It would be much easier because you don't need to use specialLabelTag.
let backbutton = MyUILabel()

Resources