How can I create a custom segment-top-tab view controller? (Swift) - ios

I have a 2 segment-top-tab-VC class, thanks to someone in SOF.
The screenshots are below.
// The 2 segment-top-tab-VC
class SegmentTabVC: UIViewController {
//MARK: - Properties
let viewControllers: [UIViewController]
init(viewControllers: [UIViewController], nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) {
self.viewControllers = viewControllers
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private let leftButton: UIButton = {
let bt = UIButton()
bt.addTarget(self, action: #selector(switchTabs), for: .touchUpInside)
bt.setAttributedTitle(NSMutableAttributedString(string: "미팅", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).darker(componentDelta: 0.4)]), for: .selected)
bt.setAttributedTitle(NSMutableAttributedString(string: "미팅", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).lighter(componentDelta: 0.4)]), for: .normal)
bt.tag = 0
return bt
}()
private let rightButton: UIButton = {
let bt = UIButton()
bt.addTarget(self, action: #selector(switchTabs), for: .touchUpInside)
bt.setAttributedTitle(NSMutableAttributedString(string: "번개", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).darker(componentDelta: 0.4)]), for: .selected)
bt.setAttributedTitle(NSMutableAttributedString(string: "번개", attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).lighter(componentDelta: 0.4)]), for: .normal)
bt.tag = 1
return bt
}()
private let contentView: UIView = {
let view = UIView()
return view
}()
var currentVC: UIViewController?
lazy var bannerTabVC = viewControllers[0]
lazy var gravitySliderTabVC = viewControllers[1]
//MARK: - Life Cycles
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
leftButton.isSelected = true
displayCurrentTab(0)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let currentVC = currentVC {
currentVC.viewWillDisappear(animated)
}
}
//MARK: - Selectors
#objc func switchTabs(_ sender: UIButton) {
self.currentVC!.view.removeFromSuperview()
self.currentVC!.removeFromParent()
switch sender.tag {
case 0:
rightButton.isSelected = false
leftButton.isSelected = true
case 1:
rightButton.isSelected = true
leftButton.isSelected = false
default:
return
}
displayCurrentTab(sender.tag)
}
//MARK: - Helpers
func configureUI() {
view.backgroundColor = .white
let stack = UIStackView(arrangedSubviews: [leftButton, rightButton])
stack.axis = .horizontal
stack.distribution = .fillEqually
view.addSubview(stack)
stack.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, right: view.rightAnchor, height: 50)
view.addSubview(contentView)
contentView.anchor(top: stack.bottomAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
}
func displayCurrentTab(_ tabIndex: Int){
if let vc = viewControllerForSelectedSegmentIndex(tabIndex) {
self.addChild(vc)
vc.didMove(toParent: self)
vc.view.frame = self.contentView.bounds
self.contentView.addSubview(vc.view)
self.currentVC = vc
}
}
func viewControllerForSelectedSegmentIndex(_ index: Int) -> UIViewController? {
var vc: UIViewController?
switch index {
case 0 :
vc = viewControllers[0]
case 1 :
vc = viewControllers[1]
default:
return nil
}
return vc
}
}
2 segment-top-tab-VC screenshot
2 segment-top-tab-VC screenshot
The above code worked.
Now, I would like to make a custom segment-top-tab-VC class for N-segments.
I'm not used to making custom classes. Can somebody help me out?
Below is the code I'm struggling with...
class Utilities {
func segmentButton(_ title: String, _ tag: Int) -> UIButton {
let bt = UIButton()
bt.setAttributedTitle(NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).darker(componentDelta: 0.4)]), for: .selected)
bt.setAttributedTitle(NSMutableAttributedString(string: title, attributes: [NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 17), NSAttributedString.Key.foregroundColor: #colorLiteral(red: 0.1393318772, green: 0.1493551731, blue: 0.2001486421, alpha: 1).lighter(componentDelta: 0.4)]), for: .normal)
bt.tag = tag
return bt
}
}
class SegmentTabVC: UIViewController {
//MARK: - Properties
let viewControllers: [UIViewController]
let titles: [String]
let buttons: [UIButton]
var currentIndex: Int
init(viewControllers: [UIViewController], titles: [String], nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) {
self.viewControllers = viewControllers
self.titles = titles
for index in 0 ..< viewControllers.count {
let button = Utilities().segmentButton(titles[index], index)
button.addTarget(self, action: #selector(switchTabs), for: .touchUpInside)
self.buttons.append(button)
}
self.currentIndex = 0
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
...
}

For anyone who struggles with the same problem in the future:
import UIKit
class SegmentTabVC: UIViewController {
//MARK: - Properties
let viewControllers: [UIViewController]
let titles: [String]
init(viewControllers: [UIViewController], titles: [String], nibName nibNameOrNil: String? = nil, bundle nibBundleOrNil: Bundle? = nil) {
self.viewControllers = viewControllers
self.titles = titles
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
var currentIndex: Int = 0
var buttons: [UIButton] = []
var topView: UIView = {
let view = UIView()
view.backgroundColor = #colorLiteral(red: 0.7254901961, green: 0.6705882353, blue: 0.9607843137, alpha: 1)
return view
}()
private let contentView = UIView()
var currentVC: UIViewController?
//MARK: - Life Cycles
override func viewDidLoad() {
super.viewDidLoad()
prepareButtons()
configureUI()
buttons[0].isSelected = true
displayCurrentTab(currentIndex)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if let currentVC = currentVC {
currentVC.viewWillDisappear(animated)
}
}
//MARK: - Selectors
#objc func switchTabs(_ sender: UIButton) {
self.currentVC!.view.removeFromSuperview()
self.currentVC!.removeFromParent()
buttons[currentIndex].isSelected = false
buttons[sender.tag].isSelected = true
currentIndex = sender.tag
displayCurrentTab(currentIndex)
}
//MARK: - Helpers
func createButton(_ index: Int) -> UIButton {
let button = Utilities().segmentButton(titles[index], index)
button.addTarget(self, action: #selector(switchTabs), for: .touchUpInside)
return button
}
func prepareButtons() {
for index in 0 ..< viewControllers.count {
buttons.append(createButton(index))
}
}
func configureUI() {
navigationController?.navigationBar.isHidden = true
view.backgroundColor = .white
let safeTopView = UIView()
safeTopView.backgroundColor = #colorLiteral(red: 0.7254901961, green: 0.6705882353, blue: 0.9607843137, alpha: 1)
view.addSubview(safeTopView)
safeTopView.anchor(top: view.topAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.topAnchor, right: view.rightAnchor)
view.addSubview(topView)
topView.anchor(top: view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, right: view.rightAnchor, height: 56)
let stack = UIStackView(arrangedSubviews: buttons)
stack.axis = .horizontal
stack.distribution = .fillEqually
view.addSubview(stack)
stack.anchor(top: topView.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, height: 44)
let dividerView = UIView()
dividerView.backgroundColor = .systemGray6
view.addSubview(dividerView)
dividerView.anchor(top: stack.bottomAnchor, left: view.leftAnchor, right: view.rightAnchor, height: 0.75)
view.addSubview(contentView)
contentView.anchor(top: dividerView.bottomAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
}
func displayCurrentTab(_ tabIndex: Int){
let selectedVC = viewControllers[tabIndex]
self.addChild(selectedVC)
selectedVC.didMove(toParent: self)
selectedVC.view.frame = self.contentView.bounds
self.contentView.addSubview(selectedVC.view)
self.currentVC = selectedVC
}
}
import UIKit
class MeetVC: SegmentTabVC {
//MARK: - Properties
init() {
super.init(viewControllers: [MY_VC_0(), MY_VC_1(), MY_VC_2(), MY_VC_3()], titles: ["MY_TITLE_0", "MY_TITLE_1", "MY_TITLE_2", "MY_TITLE_3"])
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//MARK: - Life Cycles
//MARK: - Selectors
//MARK: - Helpers
override func configureUI() {
super.configureUI()
// Whatever..
}
}

Related

context over screen while transitioning between view controllers

When its clicked on some cell, DetailViewController will be opened. Problem is that context from DetailViewController is shown while transitioning between controllers.
Picture below presents problem:
image
DetailViewController is called in function:
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
let singleMovie = movieList[indexPath.row]
getSingleMovie(movieId: singleMovie.id, completion: {})
getDirector(movieId: singleMovie.id, completion: { [weak self] in
guard let self = self else { return }
var directorName = ""
self.creditResponse?.crew.forEach({ singleCredit in
if singleCredit.knownForDepartment == .directing {
directorName = singleCredit.name
}
})
guard let safeMovie = self.detailMovie else {return}
let detailVc = DetailViewController(movie: safeMovie, groups: self.checkGroups(groups: singleMovie.genreIds), director: directorName, movieIndex: indexPath.row)
self.navigationController?.pushViewController(detailVc, animated: true)
})
}
DetailViewController:
import UIKit
class DetailViewController: UIViewController {
let movie: Details?
let groupsValue: String?
let directorValue: String?
let movieIndex: Int?
var checkButton: Bool = false
var favoriteButton: Bool = false
let backButton: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "Icon"), for: .normal)
button.addTarget(self, action: #selector(backButtonTapped), for: .touchUpInside)
return button
}()
var movieTitleLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Bold", size: 40)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
var groupsLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var directorLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Bold", size: 20)
label.textColor = .white
label.text = "Director: "
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var directorNameLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
var descriptionLabel: UILabel = {
let label = UILabel()
label.font = UIFont(name: "Quicksand-Regular", size: 20)
label.textColor = .white
label.translatesAutoresizingMaskIntoConstraints = false
label.numberOfLines = 0
return label
}()
var movieImageView: UIImageView = {
let iv = UIImageView()
iv.layer.cornerRadius = 15
iv.layer.masksToBounds = true
iv.translatesAutoresizingMaskIntoConstraints = false
return iv
}()
let buttonChecked: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.imageView?.contentMode = .scaleAspectFit
button.imageEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.addTarget(self, action: #selector(checkButtonTapped), for: .touchUpInside)
return button
}()
let buttonFavorite: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.imageView?.contentMode = .scaleAspectFit
button.imageEdgeInsets = UIEdgeInsets(top: 35, left: 35, bottom: 35, right: 35)
button.tintColor = UIColor(red: 0.475, green: 0.729, blue: 0.757, alpha: 1)
button.addTarget(self, action: #selector(favoriteButtonTapped), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear(_ animated: Bool) {
self.navigationItem.hidesBackButton = true
setupUI()
}
init(movie:Details, groups: String, director: String, movieIndex: Int) {
self.movie = movie
self.groupsValue = groups
self.directorValue = director
self.movieIndex = movieIndex
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
extension DetailViewController{
#objc func backButtonTapped() {
navigationController?.popToRootViewController(animated: true)
}
}
extension DetailViewController {
func setupUI(){
view.addSubview(movieTitleLabel)
view.addSubview(groupsLabel)
view.addSubview(directorLabel)
view.addSubview(descriptionLabel)
view.addSubview(directorNameLabel)
view.addSubview(movieImageView)
view.addSubview(backButton)
view.addSubview(buttonChecked)
view.addSubview(buttonFavorite)
setupValues()
setupConstraints()
setupButtons()
}
}
extension DetailViewController {
func setupConstraints(){
let bottomImageConstraint = movieImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
bottomImageConstraint.priority = .defaultLow
NSLayoutConstraint.activate([
movieImageView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
movieImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
movieImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bottomImageConstraint,
movieImageView.heightAnchor.constraint(equalTo: view.heightAnchor, multiplier: 0.3),
movieTitleLabel.topAnchor.constraint(equalTo: movieImageView.bottomAnchor, constant: 10),
movieTitleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
movieTitleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
groupsLabel.topAnchor.constraint(equalTo: movieTitleLabel.bottomAnchor, constant: 10),
groupsLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
directorNameLabel.topAnchor.constraint(equalTo: groupsLabel.bottomAnchor, constant: 10),
directorNameLabel.leadingAnchor.constraint(equalTo: directorLabel.trailingAnchor, constant: 10),
descriptionLabel.topAnchor.constraint(equalTo: directorLabel.bottomAnchor, constant: 10),
descriptionLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
descriptionLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor),
backButton.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 10),
backButton.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10),
backButton.heightAnchor.constraint(equalToConstant: 40),
backButton.widthAnchor.constraint(equalToConstant: 40),
buttonFavorite.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
buttonFavorite.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
buttonChecked.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 20),
buttonChecked.trailingAnchor.constraint(equalTo: buttonFavorite.trailingAnchor, constant: -50),
])
}
func setupValues(){
movieTitleLabel.text = movie?.title
guard let safeUrl = movie?.posterPath else {return}
movieImageView.downloadImage(from: safeUrl)
descriptionLabel.text = movie?.overview
groupsLabel.text = groupsValue
directorNameLabel.text = directorValue
}
func setupButtons(){
let checkValue = UserDefaults.standard.bool(forKey: "checkButton\(movieIndex ?? 0)")
let favoriteValue = UserDefaults.standard.bool(forKey: "favoriteButton\(movieIndex ?? 0)")
if checkValue == true {
buttonChecked.setImage(UIImage(systemName: "checkmark.seal.fill"), for: .normal)
}
else {
buttonChecked.setImage(UIImage(systemName: "checkmark.seal"), for: .normal)
}
if favoriteValue == true {
buttonFavorite.setImage(UIImage(systemName: "star.fill"), for: .normal)
}
else {
buttonFavorite.setImage(UIImage(systemName: "star"), for: .normal)
}
}
}
extension DetailViewController{
#objc func checkButtonTapped() {
checkButton = !checkButton
if checkButton == true {
DispatchQueue.main.async {
self.buttonChecked.setImage(UIImage(systemName: "checkmark.seal.fill"), for: .normal)
}
}
else {
DispatchQueue.main.async {
self.buttonChecked.setImage(UIImage(systemName: "checkmark.seal"), for: .normal)
}
}
UserDefaults.standard.set(checkButton, forKey: "checkButton\(movieIndex ?? 0)")
}
#objc func favoriteButtonTapped() {
favoriteButton = !favoriteButton
if favoriteButton == true {
DispatchQueue.main.async {
self.buttonFavorite.setImage(UIImage(systemName: "star.fill"), for: .normal)
}
}
else {
DispatchQueue.main.async {
self.buttonFavorite.setImage(UIImage(systemName: "star"), for: .normal)
}
}
UserDefaults.standard.set(favoriteButton, forKey: "favoriteButton\(movieIndex ?? 0)")
}
}
How to setup animations so it transits normally?
Transition GIF
It looks like your DetailViewController.view has no backgroundColor set (OR it is .clear).
Please assign a backgroundColor to your view like this.
override func viewDidLoad() {
super.viewDidLoad()
// For testing, you may want to set this to something else like `.red` / `.yellow`
// This will help you in identifying where the issue is
self.view.backgroundColor = .black
}

UIButton Array - Each Button is pointing tag 0 in CollectionView swift

I'm designing my UI programmatically, I'm creating some buttons and then showing in UICollectionView. after showing in UICollectionView when I checked, each button is pointing to tag 0.
Here's my complete code code.
class CollectionViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate{
var buttonsArray = [UIButton]()
let buttonsName = ["Start Trade", "Wallet","Profile", "My Portfolio", "Dashboard","My Transactions","My Blotter" ,"My Reports", "Forum"]
let buttonImages = ["1","2","3", "4", "5", "6", "7","8", "9"]
var collectionview: UICollectionView!
var cellId = "Cell"
override func viewDidLoad() {
super.viewDidLoad()
for i in 0..<9 {
let btn = UIButton()
btn.tag = i
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.setTitle(buttonsName[i], for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
buttonsArray.append(btn)
}
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 106, height: 97)
collectionview = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionview.dataSource = self
collectionview.delegate = self
collectionview.register(MainDashBoardCollectionViewCell.self, forCellWithReuseIdentifier: cellId)
collectionview.showsVerticalScrollIndicator = false
collectionview.backgroundColor = view.backgroundColor
self.view.addSubview(collectionview)
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
sender.animateButton(sender: sender)
print(sender.tag)
}
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return buttonsArray.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell{
let cell = collectionview.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! MainDashBoardCollectionViewCell
cell.images.image = buttonImages[indexPath.item]
cell.button = buttonsArray[indexPath.item]
cell.Label0.text = buttonsName[indexPath.item]
return cell
}
Here's My CollectionViewCell class
import UIKit
class MainDashBoardCollectionViewCell: UICollectionViewCell {
var button :UIButton = {
let btn = UIButton()
btn.frame = CGRect(x: btn.frame.width/2, y:btn.frame.width/2, width: 106, height: 97)
btn.backgroundColor = UIColor(red: 0.15, green: 0.22, blue: 0.68, alpha: 0.86)
btn.layer.cornerRadius = 5
btn.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
btn.titleLabel?.font = UIFont.setFont(of: 12)
btn.setTitleColor(UIColor(red: 0.82, green: 0.56, blue: 0.23, alpha: 1), for: .normal)
btn.translatesAutoresizingMaskIntoConstraints = false
return btn
}()
var images: UIImageView = {
let imgV = UIImageView()
imgV .translatesAutoresizingMaskIntoConstraints = false
return imgV
}()
let Label0: UILabel = {
let label = UILabel()
label.frame = CGRect(x: 0, y: 0, width: 60, height: 15)
label.textColor = #colorLiteral(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)
label.text = ""
label.font = UIFont.setFont(of: 12)
label.translatesAutoresizingMaskIntoConstraints = false
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
addSubview(images)
addSubview(button)
addSubview(Label0)
shared()
}
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
print("clicked")
print("you clicked on button \(sender.tag)")
}
func shared() {
self.contentView.addSubview(button)
self.contentView.addSubview(images)
NSLayoutConstraint.activate([
button.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 0),
button.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 0),
button.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: 0),
button.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: 0),
])
images.widthAnchor.constraint(equalToConstant: 26).isActive = true
images.heightAnchor.constraint(equalToConstant: 28).isActive = true
images.centerXAnchor.constraint(equalTo: button.centerXAnchor).isActive = true
images.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: -10).isActive = true
Label0.centerXAnchor.constraint(equalTo: images.centerXAnchor).isActive = true
Label0.centerYAnchor.constraint(equalTo: button.centerYAnchor, constant: 20).isActive = true
}
required init?(coder aDecoder: NSCoder) {
//super.init(aDecoder)
fatalError("init(coder:) has not been implemented")
}
}
is anything wrong with my code? Please help me to fix it.
Thanks in advance.
By default every UI component have tag 0, so you have to change the tag of every button.
Try this in cellForItem
cell.button.tag = indexPath.row

TextField bottom with iPhone X in Swift

I'm new in Swift, and I have an issue with the iPhone X.
I followed this tutorial: https://www.youtube.com/watch?v=FDay6ocBlnE&index=8&list=PL0dzCUj1L5JEfHqwjBV0XFb9qx9cGXwkq in order to create a Chat App.
My problem is that the textField is fixed to the bottom, and that is not good for the iPhone X.
I really don't know how I can change this, given that I'm more familiar with storyboard and here, the collectionViewController is entirely programmatically. I searched a lot of other tutorials but I found nothing to help.
This is my code:
The bottom view (with the textfield):
class ChatInputContainerView: UIView, UITextFieldDelegate {
// ...
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
// ...
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The CollectionViewController:
class ChatLogController: UICollectionViewController, UICollectionViewDelegateFlowLayout {
// ...
lazy var inputContainerView: ChatInputContainerView = {
// I can't change the y value (it changes nothing)
let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 54))
chatInputContainerView.chatLogController = self
return chatInputContainerView
}()
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
}
Update
Here's the entire code:
import UIKit
import UserNotifications
class ChatLogController: UICollectionViewController, UITextFieldDelegate, UICollectionViewDelegateFlowLayout, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
var user: Userm? {
didSet {
navigationItem.title = user?.username
loadMessages()
}
}
var messages = [Message]()
func loadMessages() {
guard let toId = user?.id else {
return
}
Api.Message.observeUserDiscussion(toId: toId) { (message) in
self.messages.append(message)
DispatchQueue.main.async(execute: {
self.collectionView?.reloadData()
//scroll to the last index
let indexPath = IndexPath(item: self.messages.count - 1, section: 0)
self.collectionView?.scrollToItem(at: indexPath, at: .bottom, animated: true)
})
}
}
let cellId = "cellId"
override func viewDidLoad() {
super.viewDidLoad()
UNUserNotificationCenter.current().getNotificationSettings { (settings) in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
//Not authorised
UIApplication.shared.registerForRemoteNotifications()
}
navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
collectionView?.contentInset = UIEdgeInsets(top: 8, left: 0, bottom: 20, right: 0)
// collectionView?.scrollIndicatorInsets = UIEdgeInsets(top: 0, left: 0, bottom: 50, right: 0)
collectionView?.alwaysBounceVertical = true
collectionView?.backgroundColor = UIColor.white
collectionView?.register(ChatMessageCell.self, forCellWithReuseIdentifier: cellId)
collectionView?.keyboardDismissMode = .interactive
arrowBackButton(greyBack)
let image = UIImage(named: "iconProfilCog")
navigationItem.rightBarButtonItem = UIBarButtonItem(image: image, style: .plain, target: self, action: #selector(handleParamsMessage))
navigationItem.rightBarButtonItem?.tintColor = UIColor(red: 203/255, green: 203/255, blue: 203/255, alpha: 1)
setupKeyboardObservers()
emptyTextField()
}
func emptyTextField() {
self.inputContainerView.inputTextField.text = ""
self.inputContainerView.sendButton.isEnabled = false
self.inputContainerView.sendButton.alpha = 0.8
}
override func viewDidLayoutSubviews() {
inputContainerView.inputTextField.roundCorners([.topLeft,.bottomLeft], radius: 10)
inputContainerView.backgroundSendButtonView.roundCorners([.topRight,.bottomRight], radius: 22)
}
lazy var inputContainerView: ChatInputContainerView = {
let chatInputContainerView = ChatInputContainerView(frame: CGRect(x: 0, y: 0, width: self.view.frame.width, height: 54))
chatInputContainerView.chatLogController = self
return chatInputContainerView
}()
func handleParamsMessage() {
print("params")
let storyboard: UIStoryboard = UIStoryboard(name: "Main", bundle: nil)
let detailMessage = storyboard.instantiateViewController(withIdentifier: "MessageDetailTableViewController") as! MessageDetailTableViewController
if let user = user {
detailMessage.userId = user.id!
self.navigationController?.pushViewController(detailMessage, animated: true)
}
}
func handleUploadTap() {
let imagePickerController = UIImagePickerController()
imagePickerController.allowsEditing = true
imagePickerController.delegate = self
//imagePickerController.mediaTypes = [kUTTypeImage as String, kUTTypeMovie as String]
present(imagePickerController, animated: true, completion: nil)
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
// if let videoUrl = info[UIImagePickerControllerMediaURL] as? URL {
// //we selected a video
// handleVideoSelectedForUrl(videoUrl)
// } else {
// //we selected an image
handleImageSelectedForInfo(info as [String : AnyObject])
// }
dismiss(animated: true, completion: nil)
}
fileprivate func handleImageSelectedForInfo(_ info: [String: AnyObject]) {
var selectedImageFromPicker: UIImage?
if let editedImage = info["UIImagePickerControllerEditedImage"] as? UIImage {
selectedImageFromPicker = editedImage
} else if let originalImage = info["UIImagePickerControllerOriginalImage"] as? UIImage {
selectedImageFromPicker = originalImage
}
if let selectedImage = selectedImageFromPicker {
HelperService.uploadMessagePictureToDatabase(selectedImage, completion: { (imageUrl) in
self.sendMessageWithImageUrl(imageUrl, image: selectedImage)
})
}
}
override var inputAccessoryView: UIView? {
get {
return inputContainerView
}
}
override var canBecomeFirstResponder : Bool {
return true
}
func setupKeyboardObservers() {
NotificationCenter.default.addObserver(self, selector: #selector(handleKeyboardDidShow), name: NSNotification.Name.UIKeyboardDidShow, object: nil)
}
override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
NotificationCenter.default.removeObserver(self)
}
func handleKeyboardDidShow() {
if messages.count > 0 {
let indexPath = IndexPath(item: messages.count - 1, section: 0)
collectionView?.scrollToItem(at: indexPath, at: .top, animated: true)
}
}
override func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return messages.count
}
override func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: cellId, for: indexPath) as! ChatMessageCell
cell.chatLogController = self
let message = messages[indexPath.item]
cell.textView.text = message.text
setupCell(cell, message: message)
//lets modify the bubbleView's width somehow???
// cell.bubbleWidthAnchor?.constant = estimateFrameForText(message.text!).width + 25
if let text = message.text {
//a text message
cell.bubbleWidthAnchor?.constant = estimateFrameForText(text).width + 25
cell.textView.isHidden = false
} else if message.imageUrl != nil {
//fall in here if its an image message
cell.bubbleWidthAnchor?.constant = 200
cell.textView.isHidden = true
}
return cell
}
fileprivate func setupCell(_ cell: ChatMessageCell, message: Message) {
if let profileImageUrl = self.user?.profileImageUrl {
let photoUrl = URL(string: profileImageUrl)
cell.profileImageView.sd_setImage(with: photoUrl)
}
if message.fromId == Api.User.CURRENT_USER?.uid {
//outgoing blue
cell.bubbleView.backgroundColor = ChatMessageCell.blueColor
cell.textView.textColor = UIColor.white
cell.profileImageView.isHidden = true
cell.tailImageView.isHidden = true
cell.bubbleViewRightAnchor?.isActive = true
cell.bubbleViewLeftAnchor?.isActive = false
} else {
//incoming gray
cell.bubbleView.backgroundColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
cell.textView.textColor = UIColor(red: 70/255, green: 70/255, blue: 70/255, alpha: 1)
cell.profileImageView.isHidden = false
cell.tailImageView.isHidden = false
cell.bubbleViewRightAnchor?.isActive = false
cell.bubbleViewLeftAnchor?.isActive = true
}
if let messageImageUrl = message.imageUrl {
let photoUrl = URL(string: messageImageUrl)
cell.messageImageView.sd_setImage(with: photoUrl)
cell.messageImageView.isHidden = false
// cell.bubbleView.backgroundColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
} else {
cell.messageImageView.isHidden = true
}
}
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
collectionView?.collectionViewLayout.invalidateLayout()
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
var height: CGFloat = 80
let message = messages[indexPath.item]
if let text = message.text {
height = estimateFrameForText(text).height + 18
} else if let imageWidth = message.imageWidth?.floatValue, let imageHeight = message.imageHeight?.floatValue {
// h1 / w1 = h2 / w2
// solve for h1
// h1 = h2 / w2 * w1
height = CGFloat(imageHeight / imageWidth * 200)
}
let width = UIScreen.main.bounds.width
return CGSize(width: width, height: height)
}
fileprivate func estimateFrameForText(_ text: String) -> CGRect {
let size = CGSize(width: 200, height: 1000)
let options = NSStringDrawingOptions.usesFontLeading.union(.usesLineFragmentOrigin)
return NSString(string: text).boundingRect(with: size, options: options, attributes: [NSAttributedStringKey.font: UIFont.systemFont(ofSize: 15, weight: .medium)], context: nil)
}
var containerViewBottomAnchor: NSLayoutConstraint?
func handleSend() {
self.inputContainerView.sendButton.isEnabled = false
let properties = ["text": inputContainerView.inputTextField.text!]
sendMessageWithPropertiesFIR(properties as [String : AnyObject])
}
fileprivate func sendMessageWithImageUrl(_ imageUrl: String, image: UIImage) {
let properties: [String: AnyObject] = ["imageUrl": imageUrl as AnyObject, "imageWidth": image.size.width as AnyObject, "imageHeight": image.size.height as AnyObject]
sendMessageWithPropertiesFIR(properties)
}
func sendMessageWithPropertiesFIR(_ properties: [String: AnyObject]) {
print(properties["text"])
var messageText = ""
if properties["text"] != nil {
messageText = properties["text"] as! String
} else {
messageText = "A envoyé une photo"
}
Api.Message.sendMessageWithProperties(toId: user!.id!, properties: properties) {
Api.Message.isUserMuted(userId: self.user!.id!, completion: { (isMuted) in
if !isMuted {
Api.UserToken.observeUserToken(withUser: self.user!.id!, completion: { (token) in
if let token = token {
Api.User.observeCurrentUser(completion: { (user) in
Api.Notification.sendNotifPush(token: token, message: "\(user.username!): \(messageText)")
})
}
})
}
})
self.emptyTextField()
}
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
handleSend()
return true
}
var startingFrame: CGRect?
var blackBackgroundView: UIView?
var startingImageView: UIImageView?
//my custom zooming logic
func performZoomInForStartingImageView(_ startingImageView: UIImageView) {
self.startingImageView = startingImageView
self.startingImageView?.isHidden = true
self.inputContainerView.inputTextField.resignFirstResponder()
startingFrame = startingImageView.superview?.convert(startingImageView.frame, to: nil)
let zoomingImageView = UIImageView(frame: startingFrame!)
zoomingImageView.backgroundColor = UIColor.red
zoomingImageView.image = startingImageView.image
zoomingImageView.isUserInteractionEnabled = true
zoomingImageView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleZoomOut)))
if let keyWindow = UIApplication.shared.keyWindow {
blackBackgroundView = UIView(frame: keyWindow.frame)
blackBackgroundView?.backgroundColor = UIColor.black
blackBackgroundView?.alpha = 0
keyWindow.addSubview(blackBackgroundView!)
keyWindow.addSubview(zoomingImageView)
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
// self.inputContainerView.inputTextField.resignFirstResponder()
self.blackBackgroundView?.alpha = 1
self.inputContainerView.alpha = 0
// math?
// h2 / w1 = h1 / w1
// h2 = h1 / w1 * w1
let height = self.startingFrame!.height / self.startingFrame!.width * keyWindow.frame.width
zoomingImageView.frame = CGRect(x: 0, y: 0, width: keyWindow.frame.width, height: height)
zoomingImageView.center = keyWindow.center
}, completion: { (completed) in
// do nothing
})
}
}
func handleZoomOut(_ tapGesture: UITapGestureRecognizer) {
if let zoomOutImageView = tapGesture.view {
//need to animate back out to controller
zoomOutImageView.layer.cornerRadius = 8
zoomOutImageView.clipsToBounds = true
UIView.animate(withDuration: 0.5, delay: 0, usingSpringWithDamping: 1, initialSpringVelocity: 1, options: .curveEaseOut, animations: {
self.startingFrame = self.startingImageView?.superview?.convert((self.startingImageView?.frame)!, to: nil)
zoomOutImageView.frame = self.startingFrame!
self.blackBackgroundView?.alpha = 0
self.inputContainerView.alpha = 1
}, completion: { (completed) in
zoomOutImageView.removeFromSuperview()
self.startingImageView?.isHidden = false
})
}
}
}
The View:
import UIKit
class ChatInputContainerView: UIView, UITextFieldDelegate {
weak var chatLogController: ChatLogController? {
didSet {
sendButton.addTarget(chatLogController, action: #selector(ChatLogController.handleSend), for: .touchUpInside)
uploadImageView.addGestureRecognizer(UITapGestureRecognizer(target: chatLogController, action: #selector(ChatLogController.handleUploadTap)))
inputTextField.addTarget(self, action: #selector(textFieldDidChange(_:)), for: .editingChanged)
}
}
let inputColor = UIColor(red: 243/255, green: 243/255, blue: 243/255, alpha: 1)
lazy var inputTextField: UITextField = {
let textField = UITextField()
textField.placeholder = "Entrer un message..."
textField.translatesAutoresizingMaskIntoConstraints = false
textField.delegate = self
textField.backgroundColor = inputColor
// textField.roundCorners([.topLeft,.bottomLeft], radius: 10)
textField.clipsToBounds = true
return textField
}()
let uploadImageView: UIImageView = {
let uploadImageView = UIImageView()
uploadImageView.isUserInteractionEnabled = true
uploadImageView.image = UIImage(named: "pinImage")
uploadImageView.translatesAutoresizingMaskIntoConstraints = false
return uploadImageView
}()
lazy var backgroundSendButtonView: UIView = {
let backgroundSendButtonView = UIView()
backgroundSendButtonView.backgroundColor = inputColor
backgroundSendButtonView.translatesAutoresizingMaskIntoConstraints = false
return backgroundSendButtonView
}()
let sendButton = UIButton(type: .system)
override init(frame: CGRect) {
super.init(frame: frame)
backgroundColor = .red
addSubview(uploadImageView)
// sendButton.setTitle("Send", for: UIControlState())
sendButton.setImage(UIImage(named: "planeChat"), for: .normal)
sendButton.backgroundColor = UIColor.white
sendButton.tintColor = UIColor(red: 82/255, green: 121/255, blue: 179/255, alpha: 1)
sendButton.layer.cornerRadius = 20
sendButton.clipsToBounds = true
sendButton.translatesAutoresizingMaskIntoConstraints = false
//what is handleSend?
addSubview(sendButton)
addSubview(self.inputTextField)
//x,y,w,h
// A enlever après
self.inputTextField.leftAnchor.constraint(equalTo: uploadImageView.rightAnchor, constant: 12).isActive = true
//self.inputTextField.leftAnchor.constraint(equalTo: leftAnchor, constant: 8).isActive = true
//self.inputTextField.centerYAnchor.constraint(equalTo: centerYAnchor).isActive = true
self.inputTextField.topAnchor.constraint(equalTo: topAnchor, constant: 4).isActive = true
self.inputTextField.rightAnchor.constraint(equalTo: sendButton.leftAnchor, constant: -4).isActive = true
self.inputTextField.heightAnchor.constraint(equalToConstant: 48).isActive = true
//x,y,w,h
sendButton.rightAnchor.constraint(equalTo: rightAnchor, constant: -8).isActive = true
sendButton.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
sendButton.widthAnchor.constraint(equalToConstant: 38).isActive = true
sendButton.heightAnchor.constraint(equalToConstant: 38).isActive = true
//x,y,w,h
uploadImageView.leftAnchor.constraint(equalTo: leftAnchor, constant: 18).isActive = true
uploadImageView.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
uploadImageView.widthAnchor.constraint(equalToConstant: 18).isActive = true
uploadImageView.heightAnchor.constraint(equalToConstant: 20).isActive = true
//l//et backgroundSendButtonView = UIView()
//addSubview(backgroundSendButtonView)
// backgroundSendButtonView.roundCorners([.topRight,.bottomRight], radius: 24)
insertSubview(backgroundSendButtonView, belowSubview: sendButton)
backgroundSendButtonView.rightAnchor.constraint(equalTo: rightAnchor, constant: -4).isActive = true
backgroundSendButtonView.centerYAnchor.constraint(equalTo: inputTextField.centerYAnchor).isActive = true
//backgroundSendButtonView.widthAnchor.constraint(equalToConstant: 30).isActive = true
backgroundSendButtonView.leftAnchor.constraint(equalTo: inputTextField.rightAnchor).isActive = true
backgroundSendButtonView.heightAnchor.constraint(equalTo: inputTextField.heightAnchor).isActive = true
//x,y,w,h
// let separatorLineView = UIView()
// separatorLineView.backgroundColor = UIColor(red: 220/255, green: 220/255, blue: 220/255, alpha: 1)
// separatorLineView.translatesAutoresizingMaskIntoConstraints = false
// addSubview(separatorLineView)
// //x,y,w,h
// separatorLineView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
// separatorLineView.topAnchor.constraint(equalTo: topAnchor).isActive = true
// separatorLineView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
// separatorLineView.heightAnchor.constraint(equalToConstant: 1).isActive = true
let gradientView = UIView()
let colorTop = UIColor.clear.cgColor
let colorBottom = UIColor(red: 0, green: 0, blue: 0, alpha: 0.05).cgColor
gradientView.translatesAutoresizingMaskIntoConstraints = false
addSubview(gradientView)
//x,y,w,h
gradientView.leftAnchor.constraint(equalTo: leftAnchor).isActive = true
gradientView.topAnchor.constraint(equalTo: topAnchor, constant: -25).isActive = true
gradientView.widthAnchor.constraint(equalTo: widthAnchor).isActive = true
gradientView.heightAnchor.constraint(equalToConstant: 25).isActive = true
gradientView.backgroundColor = UIColor.clear
let gradientBackground = CAGradientLayer()
gradientBackground.colors = [ colorTop, colorBottom]
gradientBackground.locations = [0.0, 1.0]
var backgroundLayer = CALayer()
backgroundLayer = gradientBackground
let width = UIScreen.main.bounds.size.width
backgroundLayer.frame = CGRect(x: 0, y: 0, width: width, height: 25)
print(backgroundLayer.frame)
print(gradientView.bounds)
gradientView.layer.insertSublayer(backgroundLayer, at: 0)
}
func setGradient(_ view: UIView, colorTop: CGColor, colorBottom: CGColor) {
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
chatLogController?.handleSend()
return true
}
#objc func textFieldDidChange(_ textField: UITextField) {
if textField == self.inputTextField {
if self.inputTextField.text!.isEmpty {
disableButton()
} else {
// sendButton.setTitleColor(typoGreyButton, for: .normal)
self.sendButton.isEnabled = true
self.sendButton.alpha = 1
}
}
}
func disableButton(){
//sendButton.setTitleColor(smoothGray, for: .normal)
sendButton.isEnabled = false
self.sendButton.alpha = 0.8
}
func emptyTextField() {
self.inputTextField.text = ""
disableButton()
}
// func textViewDidChange(_ textView: UITextView) {
// print(textView)
// if textView == self.inputContainerView.inputTextField {
// if (self.inputContainerView.inputTextField.text?.isEmpty)! {
// disableButton()
// } else {
// // sendButton.setTitleColor(typoGreyButton, for: .normal)
// self.inputContainerView.sendButton.isEnabled = true
// }
// }
//
// }
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If you just want to anchor to the bottom safe area, you can do that anywhere in your view controller:
if #available(iOS 11.0, *) {
someView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
} else {
someView.bottomAnchor.constraint(equalTo: bottomLayoutGuide.topAnchor).isActive = true
}
If, however, you want to use it as a constant (i.e. subtract the length of the bottom safe area from a value), you need to do that in a later lifecycle method like viewDidLayoutSubviews():
if #available(iOS 11.0, *) {
someView.bottomAnchor.constraint(equalTo: anotherView.bottomAnchor, constant: -view.safeAreaInsets.bottom).isActive = true
} else {
someView.bottomAnchor.constraint(equalTo: anotherView.bottomAnchor, constant: -bottomLayoutGuide.length).isActive = true
}
iOS 11 revamped their safe area API so make sure that you support pre-iOS-11 devices as I did in these examples.
I also just noticed that you've set the view's frame explicitly in its intializer. You typically don't want to set a view's frame like that if you're using auto layout (constraints). Therefore, I would suggest not setting the view's frame like you did and instead using constraints to do it.

Adding a UIControl into title of Navbar Programmatically - Swift

I'm trying to add a custom UI Segmented control I created into my root view controller's navbar. Here's my code:
Segmented Control:
#IBDesignable class FeedViewSC: UIControl {
fileprivate var labels = [UILabel]()
var thumbView = UIView()
var items: [String] = ["Tab1", "Tab2"] {
didSet {
setupLabels()
}
}
var selectedIndex : Int = 0 {
didSet{
displayNewSelectedIndex()
}
}
#IBInspectable var font : UIFont! = UIFont.systemFont(ofSize: 13) {
didSet {
setFont()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
func setupView() {
layer.cornerRadius = 2
layer.borderColor = UIColor(red: 2/255, green: 239/255, blue: 23/255, alpha: 1).cgColor
backgroundColor = UIColor(red: 239/255, green: 29/255, blue: 239/255, alpha: 1)
setupLabels()
insertSubview(thumbView, at: 0)
}
func setupLabels() {
for label in labels {
label.removeFromSuperview()
}
labels.removeAll(keepingCapacity: true)
for index in 1...items.count {
let label = UILabel(frame: CGRect.zero)
label.text = items[index-1]
label.textAlignment = .center
label.font = UIFont(name: "timesnr",size: 17)
label.textColor = UIColor(red: 51/255, green: 51/255, blue: 51/255, alpha: 1)
self.addSubview(label)
labels.append(label)
}
}
override func layoutSubviews() {
super.layoutSubviews()
var selectFrame = self.bounds
let newWidth = selectFrame.width / CGFloat(items.count)
selectFrame.size.width = newWidth
thumbView.frame = selectFrame
thumbView.backgroundColor = UIColor(red: 255/255, green: 255/255, blue: 255/255, alpha: 1)
thumbView.layer.cornerRadius = 5
let labelHeight = self.bounds.height
let labelWidth = self.bounds.width / CGFloat(labels.count)
for index in 0...labels.count - 1 {
let label = labels[index]
let xPosition = CGFloat(index) * labelWidth
label.frame = CGRect(x: xPosition, y: 0, width: labelWidth, height: labelHeight)
}
}
override func beginTracking(_ touch: UITouch, with event: UIEvent?) -> Bool {
let location = touch.location(in: self)
var calculatedIndex: Int?
for (index, item) in labels.enumerated() {
if item.frame.contains(location){
calculatedIndex = index
}
}
if calculatedIndex != nil {
selectedIndex = calculatedIndex!
sendActions(for: .valueChanged)
}
return false
}
func displayNewSelectedIndex (){
if(self.selectedIndex == -1){
self.selectedIndex = self.items.count-1
}
let label = labels[selectedIndex]
}
func setFont(){
for item in labels {
item.font = font
}
}
}
My VC that I would liek to add this Segmented Control to:
class FeedViewController: UIViewController {
let feedViewSC: FeedViewSC = {
let sc = FeedViewSC()
sc.translatesAutoresizingMaskIntoConstraints = false
return sc
}()
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = UIColor.white
view.addSubview(feedViewSC)
setupFeedViewSC()
}
func setupFeedViewSC() {
feedViewSC.topAnchor.constraint(equalTo: self.topLayoutGuide.bottomAnchor, constant: 5).isActive = true
feedViewSC.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
feedViewSC.heightAnchor.constraint(equalToConstant: 35).isActive = true
feedViewSC.leftAnchor.constraint(equalTo: view.leftAnchor, constant: 60).isActive = true
feedViewSC.rightAnchor.constraint(equalTo: view.rightAnchor, constant: -60).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
let img = UIImage()
self.navigationController?.navigationBar.shadowImage = img
self.navigationController?.navigationBar.setBackgroundImage(img, for: UIBarMetrics.default)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
If you can tell me how I can add my custom UIControl to my View Controller's Navigation bar title.
If the FeedViewController is the initial view controller of the NavigationController you can do it very simply by
let feedControl = FeedViewSC(frame: (self.navigationController?.navigationBar.bounds)!)
feedControl.autoresizingMask = [.flexibleWidth,.flexibleHeight]
self.navigationController?.navigationBar.addSubview(feedControl)
feedControl.addTarget(self, action: #selector(FeedViewController.changingTab), for: .valueChanged)
At least I don't see a reason that this would not work for getting it in the navigation bar.
Also not part of the question but if you are having any trouble seeing your control in IB might I suggest.
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
layer.cornerRadius = 2
layer.borderColor = UIColor(red: 2/255, green: 239/255, blue: 23/255, alpha: 1).cgColor
backgroundColor = UIColor(red: 239/255, green: 29/255, blue: 239/255, alpha: 1)
setupLabels()
insertSubview(thumbView, at: 0)
}
As for the control itself I did not test it but your events and your handling may be slightly different than value changed I am not sure.
You could also make the navigation bar of the controller a special designable class and never add it in code but you would probably have to get a reference in the viewDidLoad to use it. The designable would look like
import UIKit
#IBDesignable class DesignableNavBar: UINavigationBar {
var feedControl : FeedViewSC!
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
func setupView() {
if feedControl == nil{
feedControl = NavControl(frame: self.bounds)
feedControl.autoresizingMask = [.flexibleHeight,.flexibleWidth]
self.addSubview(feedControl)
}
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
setupView()
}
}
And then in your controller in say the viewDidLoad you could do this.
if let navController = self.navigationController{
if navController.navigationBar is DesignableNavBar{
let control = (navController.navigationBar as! DesignableNavBar). feedControl
control?.addTarget(self, action: #selector(ViewController.changingTab), for: .valueChanged)
}
}

storyboard.instantiateViewController executes for about 3 seconds

I noticed that my app has a delay before presenting UIViewController, so I used print() to find out which line of code causing it and found up that let navigationVC = storyboard!.instantiateViewController(withIdentifier: "filterNavigationVC") as! UINavigationController executes for about 2 seconds. Why might it be like that? I've tried to remove everything except this one without even navigating and still it took that long.
#IBAction func filter(_ sender: AnyObject) {
let navigationVC = storyboard!.instantiateViewController(withIdentifier: "filterNavigationVC") as! UINavigationController
let destinationVC = navigationVC.topViewController as! FilterViewController
navigationVC.modalPresentationStyle = .overCurrentContext
destinationVC.backgroundColorValue = self.view.backgroundColor!
destinationVC.color = themeColor.light
destinationVC.delegate = self
destinationVC.billModel = self.price
destinationVC.applyedFilter = self.applyedFilter
self.present(navigationVC, animated: true, completion: nil)
}
Next UIViewController's viewDidLoad()
override func viewDidLoad() {
super.viewDidLoad()
customization()
}
fileprivate func customization(){
setupBillButtons(billModel.range["low"]!, medium: self.billModel.range["medium"]!, high: self.billModel.range["high"]!)
tempConfigButtons([self.lowBillButton, self.mediumBillButton, self.highBillButton])
if color == themeColor.light {
customizeLightTheme()
} else if color == themeColor.dark {
customizeDarkTheme()
}
setCharacterSpacingForLabels([typeLabel, kitchenLabel, avarageBill, metroLabel, tagsLabel], spacing: 2.79)
setSeparatorInsets([kitchenTableView, metroTableView, typeTableView], edge: UIEdgeInsetsMake(0, 20, 0, 20))
backgroundVoew.backgroundColor = backgroundColorValue
backgroundVoew.addBackroundBlur()
self.navigationController?.navigationBar.isHidden = true
}
fileprivate func customizeDarkTheme() {
comfirmButton.setImage(UIImage(named: "comfirmLight"), for: UIControlState())
dismissButton.setImage(UIImage(named: "closeLight"), for: UIControlState())
setColorsForTableViews([kitchenTableView, metroTableView, typeTableView], separatorColor: colorWithAlpha(whiteColor, alpha: 0.05), tintColor: colorWithAlpha(whiteColor, alpha: 0.5))
setColorForLabels([kitchenLabel, avarageBill, metroLabel, typeLabel, tagsLabel], color: colorWithAlpha(whiteColor, alpha: 0.50))
topName.textColor = whiteColor
UIApplication.shared.statusBarStyle = UIStatusBarStyle.lightContent
self.view.backgroundColor = backgroundColorValue
bottomComfitmButton.tintColor = whiteColor
bottomComfitmButton.backgroundColor = colorWithAlpha(whiteColor, alpha: 0.15)
showAllTags.titleLabel!.addCharactersSpacing(-0.39, text: (self.showAllTags.titleLabel?.text)!)
showAllTags.setTitleColor(colorWithAlpha(whiteColor, alpha: 0.5), for: UIControlState())
showAllFilterImage.image = UIImage(named: "filterShowAllDark")
}
fileprivate func customizeLightTheme() {
comfirmButton.setImage(UIImage(named: "comfirmDark"), for: UIControlState())
dismissButton.setImage(UIImage(named: "closeDark"), for: UIControlState())
setColorForLabels([kitchenLabel, avarageBill, metroLabel, typeLabel, tagsLabel], color: colorWithAlpha(grayColor, alpha: 0.50))
topName.textColor = grayColor
setColorsForTableViews([kitchenTableView, metroTableView, typeTableView], separatorColor: colorWithAlpha(grayColor, alpha: 0.1), tintColor: colorWithAlpha(grayColor, alpha: 0.59))
UIApplication.shared.statusBarStyle = UIStatusBarStyle.default
bottomComfitmButton.tintColor = grayColor
bottomComfitmButton.backgroundColor = colorWithAlpha(whiteColor, alpha: 0.25)
showAllTags.titleLabel!.addCharactersSpacing(-0.39, text: (self.showAllTags.titleLabel?.text)!)
showAllTags.setTitleColor(colorWithAlpha(grayColor, alpha: 0.5), for: UIControlState())
showAllFilterImage.image = UIImage(named: "filterShowAllLight")
}
fileprivate func setupBillButtons(_ low: Bool, medium: Bool, high: Bool) {
lowBillButton.isSelected = low
mediumBillButton.isSelected = medium
highBillButton.isSelected = high
}
fileprivate func setSeparatorInsets(_ tableViews: [UITableView], edge: UIEdgeInsets) {
for tableView in tableViews {
tableView.separatorInset = edge
}
}
fileprivate func tempConfigButtons(_ buttons: [UIButton]) {
for button in buttons {
button.setTitleColor(whiteColor, for: .selected)
if self.color == themeColor.dark {
button.setBackgroundColor(colorWithAlpha(whiteColor, alpha: 0.50), forState: .selected)
button.setTitleColor(whiteColor, for: UIControlState())
DispatchQueue.main.async(execute: {
button.roundCorners([.topLeft, .topRight, .bottomLeft, .bottomRight], radius: 15, borderColor: self.colorWithAlpha(self.whiteColor, alpha: 0.50), borderWidth: 1)
})
} else {
button.setTitleColor(grayColor, for: UIControlState())
button.setBackgroundColor(colorWithAlpha(grayColor, alpha: 0.50), forState: .selected)
DispatchQueue.main.async(execute: {
button.roundCorners([.topLeft, .topRight, .bottomLeft, .bottomRight], radius: 15, borderColor: self.grayColor, borderWidth: 1)
})
}
}
}

Resources