UIViewController as rootViewController of UINavigationController causes root views buttons to move - swift4

When I do this in AppDelegate:
window?.rootViewController = {
let mainController = MenuViewController()
return mainController
}()
I get this:
But when I do this in AppDelegate:
window?.rootViewController = {
let mainController = UINavigationController(rootViewController: MenuViewController())
return mainController
}()
I get this:
Why and how do I fix? Please specify which information if more information is needed.
Here is the MenuView code that lays out the buttons manually and also sets up the properties of the buttons:
class MenuView: UIView {
//title
let titleLabel: UILabel = {
let label = UILabel()
label.text = "Survive The Attackers!!"
label.backgroundColor = UIColor.white
return label
}()
//set up buttons
let newGameButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("New Game", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
let resumeButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Resume Game", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
let highScoresButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("High Scores", for: .normal)
button.setTitleColor(UIColor.black, for: .normal)
button.backgroundColor = UIColor.white
button.layer.borderWidth = 2.0;
button.layer.borderColor = UIColor.black.cgColor
return button
}()
//add subviews and initialize the view
override init(frame: CGRect){
super.init(frame: frame)
self.backgroundColor = UIColor(patternImage: UIImage(named: "background1.png")!)
addSubview(titleLabel)
addSubview(newGameButton)
addSubview(resumeButton)
addSubview(highScoresButton)
}
required init?(coder aDecoder: NSCoder) {
fatalError("It's Apple. What did you expect?")
}
//manually layout the main menu
override func layoutSubviews() {
var cursor: CGPoint = .zero
let buttonHeight = CGFloat(40.0);
let buttonWidth = CGFloat(160.0);
let labelWidth = buttonWidth + 20;
let spacing = bounds.height/4
let titleY = 2/3 * spacing
cursor.y = titleY
cursor.x = bounds.width/2 - labelWidth/2
titleLabel.frame = CGRect(x: cursor.x, y: cursor.y, width: labelWidth, height: buttonHeight)
cursor.y = spacing
cursor.x = bounds.width/2 - buttonWidth/2
newGameButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
cursor.y += spacing
resumeButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
cursor.y += spacing
highScoresButton.frame = CGRect(x: cursor.x, y: cursor.y, width: buttonWidth, height: buttonHeight)
}
The buttons are laid out manually in layoutSubviews
Here is my MenuView controller code:
class MenuViewController: UIViewController {
var delegateID: String = UUIDVendor.vendUUID()
private var menuView: MenuView {
return view as! MenuView
}
init(){
super.init(nibName: nil, bundle: nil)
//edgesForExtendedLayout = .init(rawValue: 0)
}
required init?(coder aDecoder: NSCoder){
fatalError()
}
//loads the view in and sizes it correctly
override func loadView() {
view = MenuView()
//extendedLayoutIncludesOpaqueBars = false
}
override func viewDidLoad() {
menuView.newGameButton.addTarget(self, action: #selector(MenuViewController.newGameButtonTapped(button:)), for: .touchUpInside)
menuView.resumeButton.addTarget(self, action: #selector(MenuViewController.resumeGameButtonTapped(button:)), for: .touchUpInside)
menuView.highScoresButton.addTarget(self, action: #selector(MenuViewController.highScoreButtonTapped(button:)), for: .touchUpInside)
menuView.setNeedsLayout()
}
//fuction that handles the event when the newGameButton is tapped
#objc func newGameButtonTapped(button: UIButton){
//reset the data in the model somehow
navigationController?.pushViewController(GameViewController(), animated: true)
}
//function that handles the event when the resume game button is tapped
#objc func resumeGameButtonTapped(button: UIButton){
}
//function that handels the event when the high scores button is tapped
#objc func highScoreButtonTapped(button: UIButton){
}

call super for layoutSubviews
private var menuView: MenuView = {
let vw = MenuView()
return vw
}()
override func viewDidLoad() {
super.viewDidLoad()
view = MenuView() //Add here
//Your code
}
And remove loadView() from MenuViewController

Related

Present a Popover view from UIToolbar barButtonItem

Screenshot of the gap
I need to present a popover view from a UIBarbuttonItem in a UIToolbar. But there exists a gap between the popover view and the toolbar.
private func addToolbar() {
let toolBar = UIToolbar(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 50))
let item1 = UIBarButtonItem(image: UIImage(systemName: "pencil"), style: .plain, target: self, action: #selector(item1Pressed(_:)))
let item2 = UIBarButtonItem(image: UIImage(systemName: "house"), style: .plain, target: self, action: #selector(item2Pressed(_:)))
toolBar.sizeToFit()
toolBar.items = [item1, item2]
textField.inputAccessoryView = toolBar
}
class SearchViewController: UIViewController {
let searchBar = UISearchBar()
weak var viewControllerDelegate: SearchViewControllerDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setupView()
setupSearchbar()
}
private func setupView() {
view.backgroundColor = .white
navigationController?.navigationBar.prefersLargeTitles = true
self.title = "Search"
}
private func setupSearchbar() {
searchBar.sizeToFit()
searchBar.placeholder = "Search test"
searchBar.showsCancelButton = true
searchBar.delegate = self
self.navigationItem.titleView = searchBar
}
}
I tried setting the sourceRect of the presented popover view, but the popover view doesn't move down below a certain point.
#objc private func item1Pressed(_ sender: UIBarButtonItem) {
let vc = SearchViewController()
vc.viewControllerDelegate = self
let navVC = UINavigationController(rootViewController: vc)
navVC.modalPresentationStyle = .popover
navVC.popoverPresentationController?.delegate = self
navVC.popoverPresentationController?.permittedArrowDirections = .any
navVC.preferredContentSize = CGSize(width: 500, height: 200)
navVC.popoverPresentationController?.sourceItem = sender
var location = CGPoint(x: 0, y: 0)
if let barItemView = sender.value(forKey: "view") as? UIView {
let barFrame = barItemView.frame
let rect = barItemView.convert(barFrame, to: view)
location = rect.origin
}
navVC.popoverPresentationController?.sourceRect = CGRect(x: location.x, y: location.y+100, width: 0, height: 0)
present(navVC, animated: true)
}

iOS UIkit custom segmented buttons

I'm looking to create a view with these buttons. There is a background animation when one of the button touched.
Not sure how to do this.
Is custom segmented buttons the way to go?
I went with custom control
import UIKit
protocol MSegmentedControlDelegate:AnyObject {
func segSelectedIndexChange(to index:Int)
}
class MSegmentedControl: UIControl {
private var buttonTitles:[String]!
private var buttons: [UIButton]!
private var selectorView: UIView!
var textColor:UIColor = .black
var selectorViewColor: UIColor = .white
var selectorTextColor: UIColor = .red
weak var delegate:MSegmentedControlDelegate?
public private(set) var selectedIndex : Int = 0
convenience init(frame:CGRect,buttonTitle:[String]) {
self.init(frame: frame)
self.buttonTitles = buttonTitle
}
override func draw(_ rect: CGRect) {
super.draw(rect)
self.backgroundColor = UIColor.white
updateView()
}
func setButtonTitles(buttonTitles:[String]) {
self.buttonTitles = buttonTitles
self.updateView()
}
func setIndex(index:Int) {
buttons.forEach({ $0.setTitleColor(textColor, for: .normal) })
let button = buttons[index]
selectedIndex = index
button.setTitleColor(selectorTextColor, for: .normal)
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(index)
UIView.animate(withDuration: 0.2) {
self.selectorView.frame.origin.x = selectorPosition
}
}
#objc func buttonAction(sender:UIButton) {
for (buttonIndex, btn) in buttons.enumerated() {
btn.setTitleColor(textColor, for: .normal)
if btn == sender {
let selectorPosition = frame.width/CGFloat(buttonTitles.count) * CGFloat(buttonIndex)
selectedIndex = buttonIndex
delegate?.segSelectedIndexChange(to: selectedIndex)
UIView.animate(withDuration: 0.3) {
self.selectorView.frame.origin.x = selectorPosition
}
btn.setTitleColor(selectorTextColor, for: .normal)
}
}
}
}
//Configuration View
extension MSegmentedControl {
private func updateView() {
createButton()
configSelectorView()
configStackView()
}
private func configStackView() {
let stack = UIStackView(arrangedSubviews: buttons)
stack.axis = .horizontal
stack.alignment = .fill
stack.distribution = .fillEqually
addSubview(stack)
stack.translatesAutoresizingMaskIntoConstraints = false
stack.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
stack.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
stack.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
stack.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
private func configSelectorView() {
let selectorWidth = frame.width / CGFloat(self.buttonTitles.count)
selectorView = UIView(frame: CGRect(x: 0, y: 8, width: selectorWidth, height: 32))
selectorView.backgroundColor = selectorViewColor
selectorView.layer.cornerRadius = 16
selectorView.layer.opacity = 0.5
addSubview(selectorView)
}
private func createButton() {
buttons = [UIButton]()
buttons.removeAll()
subviews.forEach({$0.removeFromSuperview()})
for buttonTitle in buttonTitles {
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.addTarget(self, action:#selector(MSegmentedControl.buttonAction(sender:)), for: .touchUpInside)
button.setTitleColor(textColor, for: .normal)
button.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .semibold)
buttons.append(button)
}
buttons[0].setTitleColor(selectorTextColor, for: .normal)
}
}
Usage:
private let segControl: MSegmentedControl = {
let segControl = MSegmentedControl(
frame: CGRect(x: 0, y: 240, width: 280, height: 50),
buttonTitle: ["Average","Total","Pending"])
segControl.textColor = M.Colors.greyWhite
segControl.selectorTextColor = .white
return segControl
}()
To access index change event:
Implement the delegate on parent view:
addSubview(segControl)
segControl.delegate = self
Delegate:
func segSelectedIndexChange(to index: Int) {
switch index {
case 0: print("Average")
case 1: print("Total")
case 2: print("Pending")
default: break
}
}
Result:

Why don't my selector functions get called when I clicked on my radio buttons in my Xcode Swift project

I have a custom UIView class called SortView with two radio buttons and neither of their respective selector functions are being called when I click on them. I have added an instance of the SortView class to a parent class with view.addView(). Here is my custom class:
class SortView: UIView {
// MARK: - Properties
lazy var driverSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handleDriverSortRadioButton), for: .touchUpInside)
return button
}()
lazy var pickupTimeSortRadioButton: UIButton = {
let button = UIButton(type: .system)
button.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
button.setDimensions(height: 25, width: 25)
button.backgroundColor = .clear
button.contentMode = .scaleAspectFill
button.addTarget(self, action: #selector(handlePickupTimeSortRadioButton), for: .touchUpInside)
return button
}()
private let driverSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by driver:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let pickupTimeSortTitleLabel: UILabel = {
let label = UILabel()
label.text = "Sort by pickup time:"
label.textAlignment = .left
label.textColor = .white
label.font = UIFont(name: "AvenirNext-DemiBold", size: 18)
label.backgroundColor = .clear
return label
}()
private let driverSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "Louise"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private let pickupTimeSortTextField: UITextField = {
let tf = UITextField()
tf.textColor = .black
tf.textAlignment = .center
tf.placeholder = "2:00pm"
tf.font = UIFont(name: "AvenirNext-DemiBold", size: 15)
tf.backgroundColor = UIColor(white: 0.95, alpha: 1.0)
tf.setWidth(width: 150)
tf.layer.cornerRadius = 5
return tf
}()
private enum radioButtonStates {
case driver
case pickupTime
}
private var radioButtonState = radioButtonStates.driver
// MARK: - Lifecycle
override init(frame: CGRect) {
super.init(frame: frame)
configureUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Selectors
#objc func handleDriverSortRadioButton() {
print("DEBUG: driver sort radio button clicked")
if radioButtonState != .driver {
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
pickupTimeSortTextField.isEnabled = false
pickupTimeSortTextField.text = ""
radioButtonState = .driver
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
driverSortTextField.isEnabled = true
driverSortTextField.text = ""
}
}
#objc func handlePickupTimeSortRadioButton() {
print("DEBUG: pickup time sort radio button clicked")
if radioButtonState != .pickupTime {
driverSortRadioButton.setImage(UIImage(named: "Radio Button - Unselected"), for: .normal)
driverSortTextField.isEnabled = false
driverSortTextField.text = ""
radioButtonState = .pickupTime
pickupTimeSortRadioButton.setImage(UIImage(named: "Radio Button - Selected"), for: .normal)
pickupTimeSortTextField.isEnabled = true
pickupTimeSortTextField.text = ""
}
}
// MARK: - Helper Functions
private func configureUI() {
let driverSortStackView = UIStackView(arrangedSubviews: [driverSortRadioButton,
driverSortTitleLabel,
driverSortTextField])
driverSortStackView.axis = .horizontal
driverSortStackView.distribution = .fill
driverSortStackView.spacing = 5
let pickupTimeSortStackView = UIStackView(arrangedSubviews: [pickupTimeSortRadioButton,
pickupTimeSortTitleLabel,
pickupTimeSortTextField])
pickupTimeSortStackView.axis = .horizontal
pickupTimeSortStackView.distribution = .fill
pickupTimeSortStackView.spacing = 5
let stackView = UIStackView(arrangedSubviews:[driverSortStackView,
pickupTimeSortStackView])
stackView.axis = .vertical
stackView.distribution = .fill
stackView.spacing = 10
self.addSubview(stackView)
stackView.centerX(inView: self)
stackView.centerY(inView: self)
}
}
The functions which are not being called are handleDriverSortRadioButton and handlePickupTimeSortRadioButton. Here is instantiation:
private lazy var sortView = SortView()
and here is where I use it in my parent UIViewController class:
view.addSubview(sortView)
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32)
I'm going to guess that the problem is that a containing view (perhaps one of the stack views) has zero size. The result would be that its subviews are visible but not tappable.
Here is a debugging utility method you can use to track down this sort of thing:
extension UIView {
#objc func reportSuperviews(filtering:Bool = true) {
var currentSuper : UIView? = self.superview
print("reporting on \(self)\n")
while let ancestor = currentSuper {
let ok = ancestor.bounds.contains(ancestor.convert(self.frame, from: self.superview))
let report = "it is \(ok ? "inside" : "OUTSIDE") \(ancestor)\n"
if !filtering || !ok { print(report) }
currentSuper = ancestor.superview
}
}
}
Wait until your interface is all set up and the buttons are untappable, and then call that on one of the untappable buttons to get a report in the console.
Fixed it by adding with and height to the bottom stack view
sortView.anchor(top:tableView.bottomAnchor,
left: view.leftAnchor,
right: view.rightAnchor,
paddingTop: 40,
paddingLeft: 32,
paddingRight: 32,
width: view.frame.width,
height: 100)

How to add UIImageView to navigation bar in swift?

I have this code that adds a rounded border around a UIImage using UIImageView and I've used UITapGestureRecognizer to let the user tap on the button:
var profilePicture = UIImageView()
func setupUserProfileButton() {
let defaultPicture = UIImage(named: "profilePictureSmall")
profilePicture = UIImageView(image: defaultPicture)
profilePicture.layer.cornerRadius = profilePicture.frame.width / 2
profilePicture.clipsToBounds = true
profilePicture.layer.borderColor = UIColor.black.cgColor
profilePicture.layer.borderWidth = 1
// Letting users click on the image
profilePicture.isUserInteractionEnabled = true
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(profilePictureTapped))
profilePicture.addGestureRecognizer(tapGesture)
}
How can I add this to the left side of a navigation bar? Is it possible? And I don't think the tap gesture is needed if I can add the ImageView to the navigation bar as a barButtonItem, so you can ignore that. I kinda found some similar questions but they were in objective C and none of what I tried worked.
Here is what I came up with based on an answer:
import UIKit
import Firebase
class CreateStoryPage: BaseAndExtensions {
let userProfileButton = UIButton(type: .custom)
override func viewDidLoad() {
super.viewDidLoad()
// Call all the elements
setupUserProfileButton()
}
// MARK:- Setups
// Setup the user profile button
func setupUserProfileButton() {
userProfileButton.setImage(#imageLiteral(resourceName: "profilePictureSmall.png"), for: .normal)
userProfileButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
userProfileButton.addTarget(self, action: #selector(profilePictureTapped), for: .touchUpInside)
let userProfileView = UIView(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
userProfileView.layer.cornerRadius = 14
userProfileView.backgroundColor = .red
userProfileView.addSubview(userProfileButton)
let leftNavBarItem = UIBarButtonItem(customView: userProfileView)
self.navigationItem.setLeftBarButton(leftNavBarItem, animated: true)
}
// if user taps on profile picture
#objc func profilePictureTapped() {
let userProfilePage = UserProfilePage()
present(userProfilePage, animated: true, completion: nil)
}
}
Try this;
private func setupRightItem() {
let userProfileButton = UIButton(type: .custom)
userProfileButton.imageView?.contentMode = .scaleAspectFill
userProfileButton.clipsToBounds = true
userProfileButton.addTarget(self, action: #selector(profilePictureTapped), for: .touchUpInside)
userProfileButton.setImage(#imageLiteral(resourceName: "profilePictureSmall.png"), for: .normal)
userProfileButton.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
self.navigationItem.rightBarButtonItem = UIBarButtonItem(customView: userProfileButton)
userProfileButton.widthAnchor.constraint(equalToConstant: 30).isActive = true
userProfileButton.heightAnchor.constraint(equalToConstant: 30).isActive = true
}
#objc private func goProfile() {
/// -> Action
}
let navBtn = UIButton(type: .custom)
navBtn.setImage("yourImage", for: .normal)
navBtn.frame = CGRect(x: 0, y: 0, width: 28, height: 28)
navBtn.addTarget(self, action: #selector(self.openProfile(_:)), for: .touchUpInside)
let view = UIView(frame: CGRect(x: 0, y: 0, width: 28, height: 28))
view.cornerRadius = 14
view.backgroundColor = Global.colorBlue
view.addSubview(navBtn)
let leftNavBarItem = UIBarButtonItem(customView: view)
self.navigationItem.setLeftBarButton(leftNavBarItem, animated: true)
#objc
func openProfile(_ sender: UIButton) {
}

How do I prevent one UIView from being hidden by another UIView?

I'm creating a custom, reusable segmented controller using UIViews and I'm having a problem with overlapping views. It currently looks like this:
You can see that the blue selector is under the buttons but I want it to sit at the bottom and be four pixels high. To do this, I have:
let numberOfButtons = CGFloat(buttonTitles.count)
let selectorWidth = frame.width / numberOfButtons
let selectorYPosition = frame.height - 3 <--- This cause it to be hidden behind the button
selector = UIView(frame: CGRect(x: 0, y: selectorYPosition, width: selectorWidth, height: 4))
selector.layer.cornerRadius = 0
selector.backgroundColor = selectorColor
addSubview(selector)
bringSubviewToFront(selector) <--- I thought this would work but it does nothing
which results in the selector UIView being hidden behind the segment UIView (I have the Y position set to - 3 so you can see how it's being covered up. I actually want it to be - 4, but that makes it disappear entirely):
I thought using bringSubviewToFront() would bring it in front of the segment UIView but it doesn't seem to do anything. I've looked through Apple View Programming Guide and lots of SO threads but can't find an answer.
Can anybody help me see what I'm missing?
Full code:
class CustomSegmentedControl: UIControl {
var buttons = [UIButton]()
var selector: UIView!
var selectedButtonIndex = 0
var borderWidth: CGFloat = 0 {
didSet {
layer.borderWidth = borderWidth
}
}
var borderColor: UIColor = UIColor.black {
didSet {
layer.borderColor = borderColor.cgColor
}
}
var separatorBorderColor: UIColor = UIColor.lightGray {
didSet {
}
}
var commaSeparatedTitles: String = "" {
didSet {
updateView()
}
}
var textColor: UIColor = .lightGray {
didSet {
updateView()
}
}
var selectorColor: UIColor = .blue {
didSet {
updateView()
}
}
var selectorTextColor: UIColor = .black {
didSet {
updateView()
}
}
func updateView() {
buttons.removeAll()
subviews.forEach { $0.removeFromSuperview() }
// create buttons
let buttonTitles = commaSeparatedTitles.components(separatedBy: ",")
for buttonTitle in buttonTitles {
let button = UIButton(type: .system)
button.setTitle(buttonTitle, for: .normal)
button.setTitleColor(textColor, for: .normal)
button.backgroundColor = UIColor.white
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
buttons.append(button)
}
// make first button selected
buttons[0].setTitleColor(selectorTextColor, for: .normal)
let numberOfButtons = CGFloat(buttonTitles.count)
let selectorWidth = frame.width / numberOfButtons
let selectorYPosition = frame.height - 3
selector = UIView(frame: CGRect(x: 0, y: selectorYPosition, width: selectorWidth, height: 4))
selector.layer.cornerRadius = 0
selector.backgroundColor = selectorColor
addSubview(selector)
bringSubviewToFront(selector)
let stackView = UIStackView(arrangedSubviews: buttons)
stackView.axis = .horizontal
stackView.alignment = .fill
stackView.distribution = .fillEqually
addSubview(stackView)
stackView.translatesAutoresizingMaskIntoConstraints = false
stackView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
stackView.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
stackView.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
}
#objc func buttonTapped(button: UIButton) {
for (buttonIndex, btn) in buttons.enumerated() {
btn.setTitleColor(textColor, for: .normal)
if btn == button {
let numberOfButtons = CGFloat(buttons.count)
let selectorStartPosition = frame.width / numberOfButtons * CGFloat(buttonIndex)
UIView.animate(withDuration: 0.3, animations: { self.selector.frame.origin.x = selectorStartPosition })
btn.setTitleColor(selectorTextColor, for: .normal)
}
}
sendActions(for: .valueChanged)
}
}
You are covering up your selector with the stackView.
You need to do:
bringSubviewToFront(selector)
after you have added all of the views. Move that line to the bottom of updateView().