Button in UIStackView not clickable - ios

I'm trying to add a button to my stack view. The button has a buttonTapped method that should be called when it is tapped. The problem is it is never being called, the button does not seem to be clickable.
class CustomButton: UIViewController {
var buttonDelegate: ButtonDelegate?
let button = UIButton(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.size.width - 40, height: 30))
init(label: String) {
button.setTitle(label, for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
super.init(nibName: nil, bundle: nil)
}
#objc func buttonTapped() {
print("this never gets printed")
buttonDelegate?.buttonTapped(buttonType: .submit)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
button.addTarget(self, action: #selector(buttonTapped), for: .touchUpInside)
view.addSubview(button)
}
}
And then my main view controller:
protocol ButtonDelegate {
func buttonTapped(buttonType: ButtonType)
}
class DynamicViewController: UIViewController, ButtonDelegate {
lazy var scrollView: UIScrollView = {
let scrollView = UIScrollView()
scrollView.translatesAutoresizingMaskIntoConstraints = false
return scrollView
}()
lazy var stackView: UIStackView = {
let stackView = UIStackView()
stackView.axis = .vertical
stackView.distribution = .equalSpacing
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
lazy var contentView: UIView = {
let view = UIView()
view.translatesAutoresizingMaskIntoConstraints = false
return view
}()
private func setupViews() {
view.addSubview(scrollView)
scrollView.addSubview(contentView)
contentView.addSubview(stackView)
let btn = CustomButton(label: "hi")
btn.buttonDelegate = self
self.stackView.addArrangedSubview(btn.view)
}
func buttonTapped(buttonType: ButtonType) {
print("also never gets printed")
}
}
There is nothing overlapping the button or anything like that:
My question is why the button is not clickable.

You are adding the view controller as a subview. So you also need to add as a child.
Add bellow code after self.stackView.addArrangedSubview(btn.view) this line.
self.addChild(btn)
btn.didMove(toParent: self)

Related

Swift: Change value of another ViewController with delegation programmatically

I am trying to change the value of one ViewController by clicking a button inside a second ViewController with delegation.
But so far it only prints a message, but doesn't change the value.
I have this class where I defined a view:
class CounterView: UIView {
public var creditPointValue = Int()
let label = UILabel()
override init(frame: CGRect) {
super.init(frame: frame)
self.translatesAutoresizingMaskIntoConstraints = false
backgroundColor = .cyan
self.addSubview(label)
label.text = "Credit Points: \(creditPointValue)"
label.translatesAutoresizingMaskIntoConstraints = false;
label.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
label.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
}
func changeCreditPointValue(value:Int){
creditPointValue = creditPointValue + value
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am using that view inside this ViewController and I want to manipulate the variable "creditPointValue":
protocol AddCreditsDelegate {
func addCreditsToCounter()
}
class ViewController: UIViewController {
var delegate: AddCreditsDelegate?
var counterView = CounterView()
let label = UILabel()
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.backgroundColor = .white
view.addSubview(button)
button.backgroundColor = .red
button.setTitle("View2", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(changeView), for: .touchUpInside)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
override func viewDidAppear(_ animated: Bool) {
view.addSubview(counterView)
counterView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
counterView.leftAnchor.constraint(equalTo: view.leftAnchor).isActive = true
counterView.rightAnchor.constraint(equalTo: view.rightAnchor).isActive = true
//counterView.frame.size.height = 30
counterView.bottomAnchor.constraint(equalTo: counterView.topAnchor, constant: 50).isActive = true
}
#objc func changeView(){
delegate?.addCreditsToCounter()
navigationController?.pushViewController(ViewController2(), animated: true)
}
}
And inside this second ViewController I am trying to change the value by clicking the button I added to the view:
class ViewController2: UIViewController {
let button = UIButton(frame: CGRect(x: 100, y: 100, width: 100, height: 50))
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(button)
button.backgroundColor = .red
button.setTitle("add Credits", for: .normal)
button.translatesAutoresizingMaskIntoConstraints = false
button.addTarget(self, action: #selector(addCreditsButton), for: .touchUpInside)
button.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
button.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
}
#objc func addCreditsButton(){
addCreditsToCounter()
}
}
extension ViewController2 : AddCreditsDelegate{
func addCreditsToCounter() {
let vc = ViewController()
vc.delegate = self
vc.counterView.creditPointValue += 5
print("pressed")
}
}
So far only the message "pressed" gets printed each time I click the button and I don't even know if I am going into the right direction with my approach trying to use delegation.
You should not create a new ViewController instance each time you call ViewController2.addCreditsToCounter, you already have a ViewController that creates a ViewController2 instance in ViewController.changeView. Just store a weak reference to ViewController in ViewController2 using delegate. In such case ViewController (not ViewController2) should conform to AddCreditsDelegate.
First of all, replace
protocol AddCreditsDelegate {
func addCreditsToCounter()
}
with
protocol AddCreditsDelegate: AnyObject {
func addCreditsToCounter()
}
Then add weak var delegate: AddCreditsDelegate? to ViewController2 and remove ViewController.delegate. Remove ViewController2.addCreditsToCounter, ViewController2 should not conform to AddCreditsDelegate. It means that in ViewController2.addCreditsButton you should call delegate?.addCreditsToCounter(), not addCreditsToCounter().
ViewController should conform to AddCreditsDelegate:
extension ViewController: AddCreditsDelegate {
func addCreditsToCounter() {
counterView.creditPointValue += 5
print("pressed")
}
}
And don't forget to initialize ViewController2.delegate. Replace your ViewController.changeView implementation with
let controller = ViewController2()
controller.delegate = self
navigationController?.pushViewController(controller, animated: true)
You create a new instance here
let vc = ViewController()
instead you need
#objc func changeView() {
let sec = ViewController2(),
sec.delegate = self
navigationController?.pushViewController( animated: true)
}
Then declare this inside the SecondVC
weak var delegate:ViewController?
Finally
func addCreditsToCounter() {
delegate?.counterView.creditPointValue += 5
print("pressed")
}

addTarget is not working for button in UICollectionViewCell

When I m clicking on button , selector is not getting called.
There are only two component is the cell , 1 is image and another is UIButton.
Below is the code for collection cell. is there any other way to add method.
class AttachmentCell: UICollectionViewCell {
weak var delegate: AttachmentCellDelegate?
let removeButton: UIButton = {
let button = UIButton(type: .custom)
button.translatesAutoresizingMaskIntoConstraints = false
button.setImage(UIImage(named: "close_icon"), for: .normal)
button.addTarget(self, action: #selector(removeButtonTapped), for: .touchUpInside)
button.isUserInteractionEnabled = true
return button
}()
let imageView: UIImageView = {
let imgView = UIImageView()
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
return imgView
}()
override init(frame: CGRect) {
super.init(frame: frame)
self.backgroundColor = UIColor.red
self.isUserInteractionEnabled = true
self.contentView.addSubview(imageView)
imageView.isUserInteractionEnabled = true
self.contentView.addSubview(removeButton)
//self.addSubview(removeButton)
imageView.snp.makeConstraints { (make) in
make.leading.equalToSuperview()
make.trailing.equalToSuperview()
make.top.equalToSuperview()
make.bottom.equalToSuperview()
}
removeButton.snp.makeConstraints { (make) in
make.width.equalTo(40)
make.height.equalTo(40)
make.top.equalTo(imageView.snp.top).offset(-5)
make.trailing.equalTo(self.imageView.snp.trailing).offset(5)
}
self.backgroundColor = UIColor.gray
}
#objc func removeButtonTapped() {
delegate?.didRemoveButtonTapped()
}
}
Change let removeButton to lazy var removeButton.
self doesn't exist until init has been called. When you add a target to self in a let constant, you are defining it before init has been called.
Alternatively, just call addTarget in the init block.

Action not being called when button is tapped in a stack view

I have a custom view that includes a stack view. Inside the stack view I have a label and a button.
I created my stack view, label and button in the following way and added them to the parent view.
class HomeView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = false
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
...
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(HomeController.loginClicked(_:)), for: .touchUpInside)
return button
}()
}
In my view controller I add the view to the controller's base view and set the constraints. I also create the method that should be called when the signin button is tapped.
override func viewDidLoad() {
super.viewDidLoad()
homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(homeView)
homeView.fullscreenView(parentView: view)
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
When I press the button the loginClicked method is not called. Now I did tried moving the loginClicked method to the custom view and changing the addTarget accordingly and loginClicked method is called. This being said I know the button is clickable but I don't think the target for the button action is correct and that is why the loginClicked method in the view controller is not being called.
You can use Protocol/Delegation
//1. Create a protocol
protocol HomeViewDelegate{
func loginButtonClicked(sender: UIButton)
}
class HomeView: UIView {
//2. Create a delegate
var delegate: HomeViewDelegate?
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = false
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(loginClicked(sender:)), for: .touchUpInside)
button.backgroundColor = .red
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//3. Call your protocol method via delegate
#objc func loginClicked(sender: UIButton) {
if let delegate = delegate{
delegate.loginButtonClicked(sender: sender)
}
}
}
In You Caller ViewController create an extension
extension ViewController: HomeViewDelegate{
func loginButtonClicked(sender: UIButton) {
print("login Button Clicked")
}
}
First of all you set userInteractionEnabled property of your stackView to false, set it to true. Then if it does not work consider the following approach:
There are two possible ways to fix this, first is adding the target from ViewController, and the other one is using delegation.
I think the first way would be easier to implement for you.
You need to add your target from your ViewController class.
First update your view class and get rid of adding a target:
class HomeView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
translatesAutoresizingMaskIntoConstraints = false
addSubview(stackView)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
}
let stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.isUserInteractionEnabled = true
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
...
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
return button
}()
}
Now in your ViewController:
override func viewDidLoad() {
super.viewDidLoad()
homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
homeView.signIn.addTarget(self, action: #selector(loginClicked), for: .touchUpInside)
view.addSubview(homeView)
homeView.fullscreenView(parentView: view)
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
You need to add the right constraints, I had this problem, I had this problem and the solution was this.
Solution:
import Foundation
import UIKit
protocol HomeViewDelegate:class{
func loginButtonClicked(sender: UIButton)
}
class HomeView: UIView {
//2. Create a delegate
weak var delegate: HomeViewDelegate?
var stackView: UIStackView = {
let stack = UIStackView()
stack.translatesAutoresizingMaskIntoConstraints = false
stack.distribution = .fillProportionally
stack.alignment = .fill
stack.axis = .vertical
stack.isUserInteractionEnabled = true
return stack
}()
let haveAccount: UILabel = {
let label = UILabel()
label.backgroundColor = .gray
label.text = "Label"
label.textAlignment = .center
return label
}()
let signin: UIButton = {
let button = UIButton()
button.translatesAutoresizingMaskIntoConstraints = false
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(loginClicked(sender:)), for: .touchUpInside)
button.isUserInteractionEnabled = true
button.backgroundColor = .red
return button
}()
override init(frame: CGRect) {
super.init(frame: frame)
stackView.addArrangedSubview(haveAccount)
stackView.addArrangedSubview(signin)
stackView.setCustomSpacing(4.0, after: haveAccount)
addSubview(stackView)
NSLayoutConstraint.activate([
self.stackView.topAnchor.constraint(equalTo: self.topAnchor),
self.stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
self.stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
self.stackView.bottomAnchor.constraint(equalTo: self.bottomAnchor)
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
//3. Call your protocol method via delegate
#objc func loginClicked(sender: UIButton) {
if let delegate = delegate{
delegate.loginButtonClicked(sender: sender)
}
}
}
ViewController
override func viewDidLoad() {
super.viewDidLoad()
let homeView = HomeView()
homeView.translatesAutoresizingMaskIntoConstraints = false
homeView.delegate = self
self.view.addSubview(homeView)
NSLayoutConstraint.activate([
homeView.centerXAnchor.constraint(equalTo: centerXAnchor),
homeView.centerYAnchor.constraint(equalTo: centerYAnchor),
homeView.heightAnchor.constraint(equalToConstant: 300),
homeView.widthAnchor.constraint(equalToConstant: 300)
])
}
#objc func loginClicked(_ sender: UIButton) {
print("sign in button pressed")
}
add this line to your button code
button.isUserInteractionEnabled = true
re-active isUserInteractionEnabled again
let signin: UIButton = {
let button = UIButton()
button.backgroundColor = .blue
button.setTitle("Sign in", for: .normal)
button.titleLabel?.font = UIFont(name: "Avenir", size: 14)
button.setTitleColor(UIColor.white, for: .normal)
button.addTarget(self, action: #selector(ViewController.loginClicked(_:)), for: .touchUpInside)
button.isUserInteractionEnabled = true
return button
}()

Force label to display it's text

I want to include a voting function similar to the reddit app. I think a UIStackView would be perfect for this. Now I'm struggling to make the label between the vote-up and vote-down button display it's text.
I've tried to change the contentCompression to .fittingSizeLevel and to .defaultHigh but this seems to change nothing. On the image you can see, there would be plenty of space to fit the whole text, but it doesn't. What aspect am I missing?
class VotingStackView: UIStackView {
let arrowUpButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.imageView?.contentMode = .scaleAspectFit
button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 0, bottom: 16, right: 0)
button.clipsToBounds = true
return button
}()
let arrowDownButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.imageView?.contentMode = .scaleAspectFit
button.contentEdgeInsets = UIEdgeInsets(top: 16, left: 0, bottom: 16, right: 0)
button.clipsToBounds = true
return button
}()
let percentageLabel: UILabel = {
let label = UILabel()
label.text = "100%"
label.textColor = GSSettings.UI.Colors.regularTextColor
label.font = GSSettings.UI.Fonts.helveticaLight?.withSize(20)
label.clipsToBounds = false
label.setContentCompressionResistancePriority(UILayoutPriority.fittingSizeLevel, for: .horizontal)
return label
}()
var views: [UIView] = [UIView]()
//MARK: - Init & View Loading
override init(frame: CGRect) {
super.init(frame: frame)
views = [arrowUpButton, percentageLabel ,arrowDownButton]
setupStackView()
setupImages()
setupSubviews()
}
//MARK: - Setup
func setupStackView() {
self.axis = .horizontal
self.spacing = 0
self.alignment = .center
self.distribution = .fillEqually
}
func setupImages() {
let upImage = UIImage(named: "arrow_up")
let downImage = UIImage(named: "arrow_down")
arrowUpButton.setImage(upImage, for: .normal)
arrowDownButton.setImage(downImage, for: .normal)
}
func setupSubviews() {
for view in views {
addArrangedSubview(view)
}
layoutIfNeeded()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
The votingStackView is part of another StackView:
class BottomStackView: UIStackView {
let votingStackView: VotingStackView = {
let stackview = VotingStackView()
return stackview
}()
let addFriendButton: UIButton = {
let button = UIButton()
button.backgroundColor = .clear
button.tintColor = GSSettings.UI.Colors.tintColor
button.setImage(UIImage(named: "plus")?.withRenderingMode(.alwaysTemplate), for: .normal)
return button
}()
let redView: UIView = {
let view = UIView()
view.backgroundColor = .red
return view
}()
var views: [UIView] = [UIView]()
//MARK: - Init & View Loading
override init(frame: CGRect) {
super.init(frame: frame)
views = [votingStackView, addFriendButton, redView]
setupStackView()
setupSubviews()
}
//MARK: - Setup
func setupStackView() {
self.axis = .horizontal
self.spacing = 0
self.alignment = .leading
self.distribution = .fillEqually
}
func setupSubviews() {
for view in views {
addArrangedSubview(view)
}
layoutIfNeeded()
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
Change
label.setContentCompressionResistancePriority(UILayoutPriority.fittingSizeLevel, for: .horizontal)
to
label.setContentCompressionResistancePriority(UILayoutPriority.defaultHigh, for: .horizontal)
From the docs it seems that fittingSizeLevel does not take into account parent constraints, so using defaultHigh seems like the safer option.

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.

Resources