Adding a UIControl into title of Navbar Programmatically - Swift - ios

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)
}
}

Related

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

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..
}
}

UIButton in Collection View Cell not receiving Touch Up Inside event

I am trying to setup a Carousel-like UI with buttons in each carousel cell. I am using a Collection View to do this. The buttons show up in the cell, but do not seem to respond to the touch up inside event which should run tapped() and return "TAPPED!". Here is the code for the Collection View Cell which includes the button. Thanks for the help!
CarouselCollectionViewCell.swift
import UIKit
import Foundation
class CarouselCollectionViewCell: UICollectionViewCell {
static let identifier = "CarouselCollectionViewCell"
#objc func tapped() {
print("TAPPED!")
}
var mainView : UIView = {
var mainCellView = UIView()
mainCellView.translatesAutoresizingMaskIntoConstraints = false
return mainCellView
}()
var remindButton : UIButton = {
var textView = UIButton()
textView.translatesAutoresizingMaskIntoConstraints = false
return textView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(mainView)
mainView.addSubview(remindButton)
mainView.backgroundColor = .white
//Style
mainView.layer.cornerRadius = 8
mainView.layer.masksToBounds = true
// Constraints
mainView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
mainView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
mainView.heightAnchor.constraint(equalToConstant: 360).isActive = true
mainView.widthAnchor.constraint(equalToConstant: 290).isActive = true
self.remindButton.backgroundColor = UIColor(red: 69.0/255.0, green: 198.0/255.0, blue: 255.0/255.0, alpha: 1.0)
self.remindButton.layer.cornerRadius = 20
self.remindButton.layer.masksToBounds = true
self.remindButton.bottomAnchor.constraint(equalTo: self.mainView.bottomAnchor, constant: -30).isActive = true
self.remindButton.centerXAnchor.constraint(equalTo: self.mainView.centerXAnchor).isActive = true
self.remindButton.heightAnchor.constraint(equalToConstant: 50).isActive = true
self.remindButton.widthAnchor.constraint(equalToConstant: 175).isActive = true
self.remindButton.setTitle("Add Reminder", for: .normal)
self.remindButton.addTarget(self, action: #selector(self.tapped), for: .touchUpInside)
}
override func layoutSubviews() {
super.layoutSubviews()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Constraint animation with SnapKit

I am trying to implement the animation of 2 views using SnapKit.
Here is my Animation view:
class MatchAnimation: UIView {
let viewBackground: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.75)
view.alpha = 1
return view
}()
let matchView: UIView = {
let view = UIView()
return view
}()
let matchLabel: UILabel = {
let label = UILabel()
label.text = "Title"
label.textColor = .white
label.textAlignment = .center
return label
}()
let leftAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 91/2
return view
}()
let rightAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.layer.cornerRadius = 91/2
return view
}()
let goToChatButton: UIButton = {
let button = UIButton()
button.setTitle("Button", for: .normal)
button.backgroundColor = .red
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 24.5
return button
}()
init() {
super.init(frame: UIScreen.main.bounds)
viewBackground.frame = self.frame
self.addSubview(viewBackground)
self.addSubview(matchView)
matchView.addSubview(matchLabel)
matchView.addSubview(leftAvatarBg)
matchView.addSubview(rightAvatarBg)
matchView.addSubview(goToChatButton)
matchView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.center.equalToSuperview()
}
matchLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 193, height: 40))
}
leftAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(matchLabel.snp.bottom).offset(20)
make.size.equalTo(CGSize(width: 91, height: 91))
make.right.equalTo(self.snp.left).offset(0)
}
rightAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(leftAvatarBg)
make.size.equalTo(leftAvatarBg)
make.left.equalTo(self.snp.right).inset(0)
}
goToChatButton.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 171, height: 50))
make.top.equalTo(leftAvatarBg.snp.bottom).offset(25)
make.centerX.equalToSuperview()
make.bottom.equalToSuperview()
}
}
func animate() {
UIView.animate(withDuration: 5) {
self.leftAvatarBg.snp.updateConstraints { (make) in
make.right.equalTo(self.snp.left).offset(UIScreen.main.bounds.width/2+30)
}
self.rightAvatarBg.snp.updateConstraints { (make) in
make.left.equalTo(self.snp.right).inset(UIScreen.main.bounds.width/2+30)
}
self.layoutIfNeeded()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The 2 views I tried to animate are leftAvatarBg and rightAvatarBg.
Before the animation, I set them at the exterior of the screen and want to make them slide from left to right for one view and from right to left for the other.
In my controller, I just call:
func setupAnimation() {
let matchView = MatchAnimation()
view.addSubview(matchView)
matchView.animate()
}
The result of this is that the entire view is animating (scaling).
Did I miss something?
UPDATE: Thanks to swift2geek, it seems that they is a conflict between the creation of the object and the animation. In his solution, he trigger the animation by pressing a button. In my case, I want to trigger the animation as soon as possible and automatically. How can I ensure that the animation will be fired after the creation of the object ?
im not good with your bdsm SnapKit, so put the right constraints on your own. So the main reason its not working - you should separate animation and object creation.
your viewcontroller:
import UIKit
class ViewController: UIViewController {
#IBOutlet var matchButton: MatchButton!
#IBOutlet var matchView: MatchAnimation!
override func viewDidLoad() {
super.viewDidLoad()
setupAnimation()
setupButton()
}
func setupAnimation() {
matchView = MatchAnimation()
matchView.isUserInteractionEnabled = true
view.addSubview(matchView)
}
func setupButton() {
matchButton = MatchButton()
matchButton.isUserInteractionEnabled = true
matchButton.isEnabled = true
matchButton.addTarget(self, action: #selector(pressed(_:)), for: .touchUpInside)
matchView.addSubview(matchButton)
}
#objc func pressed(_ sender: MatchButton!) {
print("button tapped")
matchView.animate()
}
}
your MatchAnimation class:
import UIKit
import SnapKit
class MatchAnimation: UIView {
let viewBackground: UIView = {
let view = UIView()
view.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 0.75)
view.alpha = 1
return view
}()
let matchView: UIView = {
let view = UIView()
return view
}()
let matchLabel: UILabel = {
let label = UILabel()
label.text = "Title"
label.textColor = .white
label.textAlignment = .center
return label
}()
let leftAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .white
view.layer.cornerRadius = 91/2
return view
}()
let rightAvatarBg: UIView = {
let view = UIView()
view.backgroundColor = .blue
view.layer.cornerRadius = 91/2
return view
}()
init() {
super.init(frame: UIScreen.main.bounds)
viewBackground.frame = self.frame
self.addSubview(viewBackground)
self.addSubview(matchView)
matchView.addSubview(matchLabel)
matchView.addSubview(leftAvatarBg)
matchView.addSubview(rightAvatarBg)
matchView.snp.makeConstraints { (make) in
make.left.right.equalToSuperview()
make.center.equalToSuperview()
}
matchLabel.snp.makeConstraints { (make) in
make.top.equalToSuperview()
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 193, height: 40))
}
leftAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(matchLabel.snp.bottom).offset(20)
make.centerX.equalToSuperview()
make.size.equalTo(CGSize(width: 91, height: 91))
make.right.equalTo(self.snp.left).offset(90)
}
rightAvatarBg.snp.makeConstraints { (make) in
make.top.equalTo(leftAvatarBg)
make.size.equalTo(leftAvatarBg)
make.left.equalTo(self.snp.right).inset(120)
}
}
func animate() {
UIView.animate(withDuration: 5) {
self.leftAvatarBg.snp.updateConstraints { (make) in
make.right.equalTo(self.snp.left).offset(UIScreen.main.bounds.width/2+30)
}
self.rightAvatarBg.snp.updateConstraints { (make) in
make.left.equalTo(self.snp.right).inset(UIScreen.main.bounds.width/2+30)
}
self.layoutIfNeeded()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
and MatchButton:
import UIKit
import SnapKit
class MatchButton: UIButton {
let goToChatButton: UIButton = {
let button = UIButton()
button.setTitle("Button", for: .normal)
button.backgroundColor = .red
button.setTitleColor(.white, for: .normal)
button.layer.cornerRadius = 24.5
return button
}()
init() {
super.init(frame: UIScreen.main.bounds)
self.addSubview(goToChatButton)
goToChatButton.snp.makeConstraints { (make) in
make.size.equalTo(CGSize(width: 171, height: 50))
make.top.greaterThanOrEqualTo(100)
make.centerX.equalToSuperview()
}
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
If you want your animation to fire as soon as possible, just add layout animate() function to viewDidAppear.

UIView animation causing label to twitch

I have an IconView class that I use as a custom image for a Google Maps marker. All of the print statements show that the code is correctly executing. However, the "12:08" UILabel in circleView keeps on growing and shrinking (i.e. twitching). I can't figure out what the problem might be. I've tried manually setting the the font in the completion block, commenting out the adjustsFontSizeToFitWidth, changing the circleView to a UIButton.
import UIKit
class IconView: UIView {
var timeLabel: UILabel!
var circleView: UIView!
var clicked: Bool!
//constants
let circleViewWidth = 50.0
let circleViewHeight = 50.0
override init(frame:CGRect) {
super.init(frame : frame)
self.backgroundColor = UIColor(red: 47/255, green: 49/255, blue: 53/255, alpha: 0.0)
clicked = false
if !clicked {
//MAIN CIRCLE
print("init circle view")
circleView = UIView(frame: CGRect(x:0, y:0, width:circleViewWidth, height:circleViewHeight))
circleView.backgroundColor = UIColor(red: 47/255, green: 49/255, blue: 53/255, alpha: 1.0)
circleView.layer.cornerRadius = circleView.frame.size.height / 2.0
circleView.layer.masksToBounds = true
self.addSubview(circleView)
timeLabel = UILabel(frame: CGRect(x: 0, y: 0, width: circleViewWidth, height: circleViewHeight/3.0))
timeLabel.center = circleView.center
timeLabel.text = "12:08"
timeLabel.textAlignment = .center
timeLabel.textColor = .white
timeLabel.numberOfLines = 0
timeLabel.font = UIFont.systemFont(ofSize: 11)
timeLabel.font = UIFont.boldSystemFont(ofSize: 11)
timeLabel.adjustsFontSizeToFitWidth = true
circleView.addSubview(timeLabel)
}
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
let iconView = marker.iconView as! IconView
print("going to start animating")
if !iconView.clicked {
UIView.animate(withDuration: 0.2, animations: {
print("making this bigger now")
iconView.circleView.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
})
{ (finished:Bool) -> Void in
print("DONE")
iconView.clicked = true
}
}
return true
}

How to change borders of UIPageControl dots?

Changing the colours is pretty straightforward, but is it possible to change the border of all unselected dots?
Ex:
dot.layer.borderWidth = 0.5
dot.layer.borderColor = UIColor.blackColor()
Yes This can be done..
Actually its pretty simple.
For iOS 14 Apple has introduced a great customization, where you can set custom images and even set background
let pageControl = UIPageControl()
pageControl.numberOfPages = 5
pageControl.backgroundStyle = .prominent
pageControl.preferredIndicatorImage = UIImage(systemName: "bookmark.fill")
pageControl.setIndicatorImage(UIImage(systemName: "heart.fill"), forPage: 0)
For prior to iOS 14:-
The Pagecontrol is composed of many Subviews which you can access. self.pageControl.subviews returns you [UIView] i.e array of UIView's.
After you get a single view you can add border to it , change its borderColor, change its border width, transform the dot size like scaling it.. All those properties that a UIView has can be used.
for index in 0..<array.count{ // your array.count
let viewDot = weakSelf?.pageControl.subviews[index]
viewDot?.layer.borderWidth = 0.5
viewDot?.transform = CGAffineTransform(scaleX: 1.2, y: 1.2)
if (index == indexPath.row){ // indexPath is the current indexPath of your selected cell or view in the collectionView i.e which needs to be highlighted
viewDot?.backgroundColor = UIColor.black
viewDot?.layer.borderColor = UIColor.black.cgColor
}
else{
viewDot?.backgroundColor = UIColor.white
viewDot?.layer.borderColor = UIColor.black.cgColor
}
}
and it looks like this
And remember you do not need to set weakSelf?.pageControl.currentPage = indexPath.row.Do let me know in case of any problem.. Hope this solves your problem.
All the best
iOS 14 allows setting indicator image with SFSymbol here's my subclassing of UIPageControl
class BorderedPageControl: UIPageControl {
var selectionColor: UIColor = .black
override var currentPage: Int {
didSet {
updateBorderColor()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
currentPageIndicatorTintColor = selectionColor
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
func updateBorderColor() {
if #available(iOS 14.0, *) {
let smallConfiguration = UIImage.SymbolConfiguration(pointSize: 8.0, weight: .bold)
let circleFill = UIImage(systemName: "circle.fill", withConfiguration: smallConfiguration)
let circle = UIImage(systemName: "circle", withConfiguration: smallConfiguration)
for index in 0..<numberOfPages {
if index == currentPage {
setIndicatorImage(circleFill, forPage: index)
} else {
setIndicatorImage(circle, forPage: index)
}
}
pageIndicatorTintColor = selectionColor
} else {
subviews.enumerated().forEach { index, subview in
if index != currentPage {
subview.layer.borderColor = selectionColor.cgColor
subview.layer.borderWidth = 1
} else {
subview.layer.borderWidth = 0
}
}
}
}
}
Extension for set pagecontrol indicator border / Swift 3
extension UIImage {
class func outlinedEllipse(size: CGSize, color: UIColor, lineWidth: CGFloat = 1.0) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else {
return nil
}
context.setStrokeColor(color.cgColor)
context.setLineWidth(lineWidth)
let rect = CGRect(origin: .zero, size: size).insetBy(dx: lineWidth * 0.5, dy: lineWidth * 0.5)
context.addEllipse(in: rect)
context.strokePath()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
}
USE:
let image = UIImage.outlinedEllipse(size: CGSize(width: 7.0, height: 7.0), color: .lightGray)
self.pageControl.pageIndicatorTintColor = UIColor.init(patternImage: image!)
self.pageControl.currentPageIndicatorTintColor = .lightGray
If anybody wants to CustomUIPageControl, then might need this
#IBDesignable
class CustomPageControl: UIView {
var dotsView = [RoundButton]()
var currentIndex = 0
#IBInspectable var circleColor: UIColor = UIColor.orange {
didSet {
updateView()
}
}
#IBInspectable var circleBackgroundColor: UIColor = UIColor.clear {
didSet {
updateView()
}
}
#IBInspectable var numberOfDots: Int = 7 {
didSet {
updateView()
}
}
#IBInspectable var borderWidthSize: CGFloat = 1 {
didSet {
updateView()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func updateView() -> Void {
for v in self.subviews{
v.removeFromSuperview()
}
dotsView.removeAll()
let stackView = UIStackView()
stackView.axis = NSLayoutConstraint.Axis.horizontal
stackView.distribution = UIStackView.Distribution.fillEqually
stackView.alignment = UIStackView.Alignment.center
stackView.spacing = 10
stackView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(stackView)
//Constraints
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
NSLayoutConstraint.activate([
stackView.heightAnchor.constraint(equalToConstant: 20.0)
])
stackView.removeFullyAllArrangedSubviews()
for i in 0..<numberOfDots {
let button:RoundButton = RoundButton(frame: CGRect.zero)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.heightAnchor.constraint(equalToConstant: 10.0),
button.widthAnchor.constraint(equalToConstant: 10.0),
])
button.tag = i
button.layer.borderWidth = 1
// button.backgroundColor = circleBackgroundColor
// button.layer.borderWidth = borderWidthSize
// button.layer.borderColor = circleColor.cgColor
button.addTarget(self, action:#selector(self.buttonClicked), for: .touchUpInside)
stackView.addArrangedSubview(button)
dotsView.append(button)
}
}
func updateCurrentDots(borderColor : UIColor, backColor : UIColor, index : Int){
for button in dotsView{
if button == dotsView[index]{
button.backgroundColor = backColor
button.layer.borderColor = borderColor.cgColor
}else{
button.backgroundColor = .clear
button.layer.borderColor = borderColor.cgColor
}
}
}
#objc func buttonClicked() {
print("Button Clicked")
}
class RoundButton: UIButton {
override init(frame: CGRect) {
super.init(frame: frame)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func prepareForInterfaceBuilder() {
super.prepareForInterfaceBuilder()
}
override func layoutSubviews() {
self.layer.cornerRadius = self.frame.size.width / 2
}
}
extension UIStackView {
func removeFully(view: UIView) {
removeArrangedSubview(view)
view.removeFromSuperview()
}
func removeFullyAllArrangedSubviews() {
arrangedSubviews.forEach { (view) in
removeFully(view: view)
}
}
}
You can use either Programmatically or Stoaryboard
To update the current dots calls this function
self.pageControl.updateCurrentDots(borderColor: .white, backColor: .white, index:1)

Resources