View design not showing in ViewController - ios

I'm trying to create a UIView and set it as a ViewController's view.
I did set it up but it won't show properly. for example, I chose a backgroundColor for the view, which is not showing. I added two buttons and they do show, but not as expected.
This is my UIView code:
import UIKit
public final class LoginView: UIView {
public override init(frame: CGRect) {
super.init(frame: frame)
self.addSubview(stackView)
self.backgroundColor = UIColor.appColors.white
NSLayoutConstraint.activate([
stackView.centerXAnchor.constraint(equalTo: self.centerXAnchor),
stackView.centerYAnchor.constraint(equalTo: self.centerYAnchor),
stackView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
stackView.trailingAnchor.constraint(equalTo: self.trailingAnchor),
])
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public let login: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 17.0)
button.setTitle("Login", for: .normal)
button.setTitleColor(UIColor.appColors.white, for: .normal)
button.backgroundColor = UIColor.appColors.green
button.contentHorizontalAlignment = .center
button.layer.cornerRadius = 10
button.layer.shadowColor = UIColor(white: 0, alpha: 0.25).cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 0
button.layer.masksToBounds = false
button.clipsToBounds = true
return button
}()
public let signup: UIButton = {
let button = UIButton(type: .system)
button.titleLabel?.font = UIFont(name: "AvenirNext-Regular", size: 17.0)
button.setTitle("Signup", for: .normal)
button.setTitleColor(UIColor.appColors.white, for: .normal)
button.backgroundColor = UIColor.appColors.red
button.contentHorizontalAlignment = .center
button.layer.cornerRadius = 10
button.layer.shadowColor = UIColor(white: 0, alpha: 0.25).cgColor
button.layer.shadowOffset = CGSize(width: 0, height: 2)
button.layer.shadowOpacity = 1
button.layer.shadowRadius = 0
button.layer.masksToBounds = false
button.clipsToBounds = true
return button
}()
private lazy var stackView: UIStackView = {
let stackView = UIStackView(arrangedSubviews: [self.login, self.signup])
stackView.axis = .vertical
stackView.distribution = .fillEqually
stackView.alignment = .fill
stackView.spacing = 10.0
stackView.translatesAutoresizingMaskIntoConstraints = false
return stackView
}()
}
This is my ViewController:
import UIKit
class loginVC: UIViewController {
override func loadView() {
super.loadView()
self.view = LoginVC()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
As you can see, I did set up everything, and I did changed the look of the buttons and chose a backgorund color, but it just won't show on the ViewController.
I want to seperate the View and the ViewController because I really need flexability with this app, but it just won't work.
Any ideas?

In loadView(), it must be LoginView() instead of loginVC(), i.e
class loginVC: UIViewController {
override func loadView() {
super.loadView()
self.view = LoginView()
}
override func viewDidLoad() {
super.viewDidLoad()
}
}
Edit:
Try replacing UIColor.appColors with just UIColor and see if it giving the expected result. This is the only thing I changed in your code to get that working perfectly.

Related

How can I make this gray area go away (iOS UIKit)?

My iOS app, built with UIKit, has this gray area at the top and I don't know how to make it go away. Here is the code for this screen:
app screenshot
import Foundation
import UIKit
class HomeController: UIViewController {
//MARK: - Properties
private let topStack = HomeNavigationStackView()
private let bottomStack = BottomControlsStackView();
private let deckView: UIView = {
let view = UIView()
view.backgroundColor = .systemPink
view.layer.cornerRadius = 5
return view
}()
//MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
configureUI()
configureCards()
}
//MARK: - Helpers
func configureCards(){
let user1 = User(name: "Jane Doe", age: 22, images: [#imageLiteral(resourceName: "jane1"),#imageLiteral(resourceName: "appSampleProfile")])
let user2 = User(name: "Megan", age: 34, images: [#imageLiteral(resourceName: "kelly2"),#imageLiteral(resourceName: "appSampleProfile")])
let cardView1 = CardView(viewModel: CardViewModel(user: user1))
let cardView2 = CardView(viewModel: CardViewModel(user: user2))
deckView.addSubview(cardView1)
deckView.addSubview(cardView2)
cardView1.fillSuperview()
cardView2.fillSuperview()
}
func configureUI(){
view.backgroundColor = .white
let stack = UIStackView(arrangedSubviews: [topStack, deckView, bottomStack])
stack.axis = .vertical
view.addSubview(stack)
stack.anchor(top:view.safeAreaLayoutGuide.topAnchor, left: view.leftAnchor, bottom: view.safeAreaLayoutGuide.bottomAnchor, right: view.rightAnchor)
stack.isLayoutMarginsRelativeArrangement = true
stack.layoutMargins = .init(top: 0, left: 12, bottom: 0, right: 12)
stack.bringSubviewToFront(deckView)
}
}
And here is the code for the navigation items at the top:
import UIKit
class HomeNavigationStackView: UIStackView {
// MARK: - Properties
let settingsButton = UIButton(type: .system)
let messageButton = UIButton(type: .system)
let tinderIcon = UIImageView(image: #imageLiteral(resourceName: "app_icon"))
//MARK: - Lifecycle
override init(frame: CGRect){
super.init(frame: frame)
heightAnchor.constraint(equalToConstant: 80).isActive = true
tinderIcon.contentMode = .scaleAspectFit
settingsButton.setImage(#imageLiteral(resourceName: "top_left_profile").withRenderingMode(.alwaysOriginal), for: .normal)
messageButton.setImage(#imageLiteral(resourceName: "top_right_messages").withRenderingMode(.alwaysOriginal), for: .normal)
[settingsButton, UIView(), tinderIcon, UIView(), messageButton].forEach{
view in addArrangedSubview(view)
}
distribution = .equalCentering
isLayoutMarginsRelativeArrangement = true
layoutMargins = .init(top:0, left:16, bottom:0, right:16)
}
required init(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
you have embedded your view controller to a NavigationController in order to perform segues. You can hide the navigationBar and set your own Items like this:
navigationController?.navigationBar.tintColor = UIColor.clear
navigationController?.navigationBar.isHidden = false
navigationController?.navigationBar.backgroundColor = UIColor.clear
Or if you want to leave it and the thing that bothers you is the shadow and gray color then you can use this code. It removes the shadow on the bottom of navigationBar (Gray Bar) and sets the backGrounColor to clear, but it leaves you ability to set the titleColor, tile etc.
view.backgroundColor = UIColor.white
navigationController?.navigationBar.tintColor = UIColor.black
navigationController?.navigationBar.shadowImage = UIImage()
navigationController?.navigationBar.backgroundColor = UIColor.clear
navigationController?.navigationBar.isTranslucent = true
navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)

Setting Label below Navigation Bar programmatically in Xcode

recently, I decided to quit using storyboards on my IOS app. So, am presently learning everything now with code. I was trying to place a label below navigationBar view but I got an error and I don't know how to debug it. Please view my code and share your thoughts.
class SellBaseViewController: UIViewController {
lazy var container: UIStackView = {
let stackView = UIStackView(frame: .zero)
stackView.alignment = .fill
stackView.axis = .vertical
stackView.spacing = 2
stackView.willSetConstraints()
return stackView
}()
lazy var navHeader: UIView! = {
return self.navBar()
}()
lazy var firstLabel: UILabel! = {
return self.labelOne()
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
if !Authentication.shared.isAuthenticated {
showLogin()
} else {
self.setupInterface()
}
}
private func setupInterface() {
self.navigationController?.navigationBar.isHidden = true
self.embedInScrollView(content: self.container)
navHeader.willSetConstraints()
firstLabel.willSetConstraints()
self.container.addArrangedSubviews([self.navHeader!, self.firstLabel!])
DispatchQueue.main.async {
NSLayoutConstraint.activate([
self.navHeader.heightAnchor.constraint(equalToConstant: 44),
self.navHeader.widthAnchor.constraint(equalTo: self.view.widthAnchor),
self.navHeader.topAnchor.constraint(equalTo: self.container.topAnchor),
])
}
}
// MARK: NAVBAR
func navBar() -> UIView {
let navBar = UIView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: self.view.frame.width, height: 44)))
navBar.backgroundColor = UIColor.constants.darkBlue
let backIcon = UIImage(named: "ic_back")?.withRenderingMode(.alwaysTemplate)
let returnButton = UIButton(type: .custom)
returnButton.imageView?.tintColor = UIColor.white
returnButton.setImage(backIcon, for: .normal)
returnButton.image(for: .normal)
returnButton.titleLabel?.font = UIFont(name: "Hind", size: 18)
returnButton.setTitle("Sell", for: .normal)
returnButton.setTitleColor(UIColor.white, for: .normal)
returnButton.addTarget(self, action: #selector(self._return), for: .touchUpInside)
returnButton.willSetConstraints()
navBar.addSubviews([returnButton])
NSLayoutConstraint.activate([
returnButton.centerYAnchor.constraint(equalTo: navBar.centerYAnchor),
returnButton.leadingAnchor.constraint(equalTo: navBar.leadingAnchor, constant: 11),
returnButton.heightAnchor.constraint(equalToConstant: 24),
returnButton.widthAnchor.constraint(equalToConstant: 71),
])
return navBar
}
func labelOne() -> UILabel{
let label = UILabel()
label.textAlignment = .center
label.textColor = .white
label.font = UIFont(name: "Avenir-Light", size: 15.0)
label.text = "This is a Label"
self.view.addSubview(label)
return labelOne()
}
#objc func _return() {
self.backHome()
}
}
The navBar showed well, but when I added the label, the app kept crashing with this error. I don't know how to find out exactly what the error is:
Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffee6703fe8)
func labelOne() -> UILabel{
let label = UILabel()
label.textAlignment = .center
label.textColor = .white
label.font = UIFont(name: "Avenir-Light", size: 15.0)
label.text = "This is a Label"
self.view.addSubview(label)
return labelOne()
}
You did:
return labelOne()
You should do:
return label

Transparent gradient not working in UIView Class

I'm trying to add a transparent gradient to UIView in UIView Class but it doesn't work.
class RecipesDetailsView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .white
let gradientMaskLayer = CAGradientLayer()
gradientMaskLayer.frame = containerView.bounds
gradientMaskLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor]
gradientMaskLayer.locations = [0, 1]
containerView.layer.mask = gradientMaskLayer
containerView.fadeView(style: .bottom, percentage: 0.5)
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var startCookingButton: UIButton = {
let startCookingButton = UIButton(type: .system)
startCookingButton.setTitle("Start cooking", for: .normal)
startCookingButton.setTitleColor(.white, for: .normal)
startCookingButton.backgroundColor = .CustomGreen()
startCookingButton.layer.cornerRadius = 8.0
startCookingButton.translatesAutoresizingMaskIntoConstraints = false
startCookingButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
return startCookingButton
}()
lazy var saveButton: UIButton = {
let saveButton = UIButton(type: .system)
saveButton.setTitleColor(.customDarkGray(), for: .normal)
saveButton.setTitle("Save", for: .normal)
saveButton.setImage(UIImage(systemName: "heart"), for: .normal)
saveButton.imageEdgeInsets = UIEdgeInsets(top: 0,left: -5,bottom: 0,right: 0)
saveButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: -5)
saveButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
saveButton.tintColor = .customDarkGray()
saveButton.backgroundColor = .clear
saveButton.translatesAutoresizingMaskIntoConstraints = false
return saveButton
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
containerView.heightAnchor.constraint(equalToConstant: frame.width / 5)
])
}
func setupStartCookingButton() {
NSLayoutConstraint.activate([
startCookingButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
startCookingButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -32),
startCookingButton.heightAnchor.constraint(equalToConstant: 55),
startCookingButton.widthAnchor.constraint(equalToConstant: frame.width * (2.5/4))
])
}
func setupSaveButtonConstraints() {
NSLayoutConstraint.activate([
saveButton.centerYAnchor.constraint(equalTo: startCookingButton.centerYAnchor),
saveButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
saveButton.heightAnchor.constraint(equalTo: startCookingButton.heightAnchor),
saveButton.widthAnchor.constraint(equalToConstant: frame.width * (1.2/4))
])
}
func addSubviews() {
addSubview(containerView)
containerView.addSubview(startCookingButton)
containerView.addSubview(saveButton)
}
func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupStartCookingButton()
setupSaveButtonConstraints()
}
}
What I want to get:
What I get from my code:
Layers do not "auto-size" with their views, so you need to keep that gradient layer as a property and update its frame when the view layout changes.
Add this property:
private var gradientMaskLayer: CAGradientLayer!
then, in lazy var containerView: UIView = change:
let gradientMaskLayer = CAGradientLayer()
to:
gradientMaskLayer = CAGradientLayer()
then, add this func:
override func layoutSubviews() {
super.layoutSubviews()
gradientMaskLayer.frame = bounds
}
Edit
However, that will apply the gradient mask to containerView AND its subviews (the buttons), which is probably not what you want.
So, change your addSubviews() func to:
func addSubviews() {
addSubview(containerView)
// add buttons to self, not to containerView
//containerView.addSubview(startCookingButton)
//containerView.addSubview(saveButton)
addSubview(startCookingButton)
addSubview(saveButton)
}
Edit 2
Here is a complete implementation, with the view controller's background set to red:
class TestViewController: UIViewController {
var rv: RecipesDetailsView!
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .red
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// with the way you are setting up the layout,
// we need to add the view here where we know the
// frame has been setup
if rv == nil {
let w = view.frame.width
let h = w / 5.0 * 2.0
let t = view.frame.height - h
rv = RecipesDetailsView(frame: CGRect(x: 0.0, y: t, width: w, height: h))
view.addSubview(rv)
}
}
}
class RecipesDetailsView: UIView {
// add this var / property
private var gradientMaskLayer: CAGradientLayer!
override init(frame: CGRect) {
super.init(frame: frame)
layoutUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
super.layoutSubviews()
// layers do not follow frame changes, so update here
gradientMaskLayer.frame = bounds
}
lazy var containerView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .white
gradientMaskLayer = CAGradientLayer()
gradientMaskLayer.frame = containerView.bounds
gradientMaskLayer.colors = [UIColor.clear.cgColor, UIColor.white.cgColor]
gradientMaskLayer.locations = [0, 1]
containerView.layer.mask = gradientMaskLayer
//containerView.fadeView(style: .bottom, percentage: 0.5)
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
lazy var startCookingButton: UIButton = {
let startCookingButton = UIButton(type: .system)
startCookingButton.setTitle("Start cooking", for: .normal)
startCookingButton.setTitleColor(.white, for: .normal)
//startCookingButton.backgroundColor = .CustomGreen()
startCookingButton.backgroundColor = .systemGreen
startCookingButton.layer.cornerRadius = 8.0
startCookingButton.translatesAutoresizingMaskIntoConstraints = false
startCookingButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
return startCookingButton
}()
lazy var saveButton: UIButton = {
let saveButton = UIButton(type: .system)
//saveButton.setTitleColor(.customDarkGray(), for: .normal)
saveButton.setTitleColor(.darkGray, for: .normal)
saveButton.setTitle("Save", for: .normal)
saveButton.setImage(UIImage(systemName: "heart"), for: .normal)
saveButton.imageEdgeInsets = UIEdgeInsets(top: 0,left: -5,bottom: 0,right: 0)
saveButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 0,bottom: 0,right: -5)
saveButton.titleLabel?.font = UIFont(name: "AvenirNext-Bold", size: 14)
//saveButton.tintColor = .customDarkGray()
saveButton.tintColor = .darkGray
saveButton.backgroundColor = .clear
saveButton.translatesAutoresizingMaskIntoConstraints = false
return saveButton
}()
func setupContainerViewConstraints() {
NSLayoutConstraint.activate([
containerView.bottomAnchor.constraint(equalTo: bottomAnchor),
containerView.leadingAnchor.constraint(equalTo: leadingAnchor),
containerView.trailingAnchor.constraint(equalTo: trailingAnchor),
containerView.heightAnchor.constraint(equalToConstant: frame.width / 5)
])
}
func setupStartCookingButton() {
NSLayoutConstraint.activate([
startCookingButton.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 16),
startCookingButton.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -32),
startCookingButton.heightAnchor.constraint(equalToConstant: 55),
startCookingButton.widthAnchor.constraint(equalToConstant: frame.width * (2.5/4))
])
}
func setupSaveButtonConstraints() {
NSLayoutConstraint.activate([
saveButton.centerYAnchor.constraint(equalTo: startCookingButton.centerYAnchor),
saveButton.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
saveButton.heightAnchor.constraint(equalTo: startCookingButton.heightAnchor),
saveButton.widthAnchor.constraint(equalToConstant: frame.width * (1.2/4))
])
}
func addSubviews() {
addSubview(containerView)
// add buttons to self, not to containerView
//containerView.addSubview(startCookingButton)
//containerView.addSubview(saveButton)
addSubview(startCookingButton)
addSubview(saveButton)
}
func layoutUI() {
addSubviews()
setupContainerViewConstraints()
setupStartCookingButton()
setupSaveButtonConstraints()
}
}
Result:

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.

Resources