I am currently trying to build a label using the help of Snapkit.
This label should replicate the data given to it from an external source in the UI.
This is the code I currently have.
I would like to incorporate an IBOutlet and change the Int that is represented in the circle.
I am trying to create this in my own custom class. Please see my code thus far below.
Thanks
import Foundation
import UIKit
class NumberInCircleView: UIView {
var numberLabel: UILabel!
override init(frame: CGRect) {
super.init(frame: frame)
self.setupViews()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.setupViews()
}
func setupViews() {
self.numberLabel = UILabel(frame: .zero)
self.addSubview(self.numberLabel)
self.numberLabel.snp.makeConstraints { (make) in
make.edges.equalToSuperview()
}
self.layer.cornerRadius = 30
self.backgroundColor = UIColor.red
self.layer.borderWidth = 1
self.layer.borderColor = UIColor.black.cgColor
self.numberLabel.text = "1"
self.numberLabel.textAlignment = .center
self.numberLabel.textColor = UIColor.white
self.numberLabel.adjustsFontSizeToFitWidth = true
self.numberLabel.minimumScaleFactor = 0.5
}
}
You can set the integer by making it string.
var count:Int = 0
self.numberLabel.text = "\(count)"
Say you have a count variable:
var count = 0
If you need to change the value displayed in your numberLabel (which should be declared as #IBOutlet weak var numberLabel: UILabel! at the top of your class if it's an IBOutlet) using this count variable, then you would simply go for:
let customView = NumberInCircleView()
customView.numberLabel.text = "\(count)"
or:
customView.numberLabel.text = String(describing: count)
Whenever you need to update your label's text using the count.
Related
I am returning a single UILabel within a UIImageView, within a Cell for a UICollectionViewController. My didSelectItem and dynamic width or working for the cell.
However I am trying to make the UIImageView a capsule shape, the below code I thought would return a circle but isn't. What I want to make is a capsule background layer for the Text, similar to a button but not a button.
class CategoryView: UIView {
private let capsuleBackground: UIImageView = {
let view = UIImageView()
view.layer.backgroundColor = UIColor.white.cgColor
view.layer.borderWidth = 1
view.layer.borderColor = COLOR_PRIMARY?.cgColor
view.layer.cornerRadius = view.frame.size.width/2
view.translatesAutoresizingMaskIntoConstraints = false
view.clipsToBounds = true
view.isUserInteractionEnabled = false
return view
}()
private let textLabel: CustomUILabel = {
let label = CustomUILabel(title: "Hello World")
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
fileprivate func setup() {
addSubview(capsuleBackground)
capsuleBackground.addSubview(textLabel)
capsuleBackground.centerInSuperview()
textLabel.edges(to: capsuleBackground, insets: TinyEdgeInsets(top: 8, left: 8, bottom: 8, right: 8))
}
}
this is what it looks like
view.layer.cornerRadius = 12
🤦♂️
I feel this is pretty simple to accomplish but I can't seem to figure it out. I'm fairly new to not using the storyboard and trying to learn how to set my constraints programatically for my views. I created the view that I want easily in storyboard but can't seem to get it programatically.
I have my view controller has my parent view, and then I call a container view. I imagine in the container view is where I setup my constraints but I can't get the height of my view to stay the same every-time I change to a different device
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var clariView = ClariContainerView()
view.addSubview(clariView)
}
}
This my view controller and then my ClariContainerView looks like this:
class ClariContainerView: UIView {
lazy var clariQuestionView: UIView = {
let containerView = UIView(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: 0))
containerView.backgroundColor = .blue
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
public func setupView() {
addSubview(clariQuestionView)
setupLayout()
}
public func setupLayout() {
NSLayoutConstraint.activate([
clariQuestionView.heightAnchor.constraint(equalToConstant: 169)
])
}
}
What I'm trying to recreate is this:
I need the height of the blue view to always be 169.
Here is how you would do that:
First, you don't need to define a frame for your containerView since the translatesAutoresizingMaskIntoConstraints = falsestatement is specifying that you'll be using auto-layout and therefore the frame will be ignored:
lazy var clariQuestionView: UIView = {
let containerView = UIView()
containerView.backgroundColor = .blue
containerView.translatesAutoresizingMaskIntoConstraints = false
return containerView
}()
And here is how you would define your constraints. You need to set height, but also need to pin the view to the bottom, the leading, and the trailing edges of self.view:
public func setupLayout() {
NSLayoutConstraint.activate([
clariQuestionView.heightAnchor.constraint(equalToConstant: 169),
clariQuestionView.bottomAnchor.constraint(equalTo: self.view.bottomAnchor),
clariQuestionView.leadingAnchor.constraint(equalTo: self.view.leadingAnchor),
clariQuestionView.trailingAnchor.constraint(equalTo: self.view.trailingAnchor)
])
}
For such a basic layout you don't really need to add heightAnchor. Here is a simple way to achieve desired behavior + bonus — a code snippet to adjust height according to the device's safeAreaInsets.
class ClariContainerView: UIView {
lazy var clariQuestionView: UIView = {
let desiredContainerHeigh = 169
// If you want, you can use commented code to adjust height according to the device's safe area.
// This might be needed if you want to keep the same height over safe area on all devices.
// let safeAreaAdjustment = UIApplication.shared.keyWindow?.rootViewController?.view.safeAreaInsets.bottom ?? 0
let containerView = UIView(frame: CGRect(x: 0, y: UIScreen.main.bounds.height - 169, width: UIScreen.main.bounds.width, height: 169))
containerView.backgroundColor = .blue
containerView.translatesAutoresizingMaskIntoConstraints = true
return containerView
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setupView()
}
public func setupView() {
addSubview(clariQuestionView)
}
}
I have spent a long time trying to stack the views that I create programmatically. I looked at examples from Add views in UIStackView programmatically but that didn't work. Listed below is the code, I am calling the setUpListings from the view controller. There are two entries but only one entry is shown.
import UIKit
import SnapKit
class ListingsView : UIView {
var containerView: UIView!
var listingsContainerView: UIStackView!
init() {
super.init(frame: CGRect.zero)
setUpContainerView()
setUpListingsContainer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
make.height.equalTo(self)
make.width.equalTo(self)
containerView.backgroundColor = UIColor.white
}
}
func setUpListingsContainer() {
listingsContainerView = UIStackView()
listingsContainerView.distribution = .equalSpacing
listingsContainerView.alignment = .fill
listingsContainerView.axis = .vertical
listingsContainerView.spacing = 10
listingsContainerView.translatesAutoresizingMaskIntoConstraints = false
containerView.addSubview(listingsContainerView)
listingsContainerView.snp.makeConstraints { (make) in
make.top.equalTo(containerView)
make.left.equalTo(containerView)
make.bottom.equalTo(containerView)
make.right.equalTo(containerView)
}
}
func setUpListings(listings: [Listing]) {
for listing in listings {
let listingEntry = ListingEntry(listingId: listing.id)
listingsContainerView.addArrangedSubview(listingEntry)
}
}
class ListingEntry : UIView {
var listingId: String?
var containerView: UIView!
init(listingId: String) {
super.init(frame: CGRect.zero)
self.listingId = listingId
self.setUpContainerView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
containerView.backgroundColor = UIColor.gray
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
make.width.equalTo(150)
make.height.equalTo(150)
}
}
}
}
The view currently looks like
But the blocks should be stacked.
Couple things...
First, I'd suggest learning how constraints and auto-layout work before using something like SnapKit. It can make some things easier --- but until one has a good understanding of the fundamentals, it's not clear what's doing what.
Second, during development, it helps to give views and subviews contrasting background colors. Makes it much easier to see what's happening to the frames at run-time.
So, if you're going to stick with SnapKit...
Try to keep code "clean." That is, don't put anything inside a snp.makeConstraints block that isn't directly related (such as setting background colors).
In your ListingEntry class, you're adding a subview (containerView) and giving that view a width and height of 150, but you are not constraining it to its superview... which results in a view height of Zero.
Take a look at the modifications I made to your code. I added comments that should make the changes clear:
class MiscViewController: UIViewController {
var listingsView: ListingsView = {
let v = ListingsView()
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
view.addSubview(listingsView)
listingsView.backgroundColor = .red
// constrain listingsView to all 4 sides with 40-pt "padding"
listingsView.snp.makeConstraints { (make) in
make.top.bottom.leading.trailing.equalToSuperview().inset(40.0)
}
let listings: [Listing] = [
Listing(id: "A"),
Listing(id: "B"),
Listing(id: "C"),
]
listingsView.setUpListings(listings: listings)
}
}
struct Listing {
var id: String = ""
}
class ListingsView : UIView {
var containerView: UIView!
var listingsContainerView: UIStackView!
init() {
super.init(frame: CGRect.zero)
// probably want to set clipsToBounds so any content doesn't extend outside the frame
clipsToBounds = true
setUpContainerView()
setUpListingsContainer()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
self.addSubview(containerView)
containerView.backgroundColor = UIColor.green
// constrain containerView to all 4 sides
containerView.snp.makeConstraints { (make) in
make.top.bottom.leading.trailing.equalToSuperview()
}
}
func setUpListingsContainer() {
listingsContainerView = UIStackView()
listingsContainerView.distribution = .equalSpacing
listingsContainerView.alignment = .fill
listingsContainerView.axis = .vertical
listingsContainerView.spacing = 10
containerView.addSubview(listingsContainerView)
// constrain listingsContainerView (a stack view) to all 4 sides
listingsContainerView.snp.makeConstraints { (make) in
make.top.leading.bottom.trailing.equalToSuperview()
}
}
func setUpListings(listings: [Listing]) {
for listing in listings {
let listingEntry = ListingEntry(listingId: listing.id)
listingEntry.backgroundColor = .cyan
listingsContainerView.addArrangedSubview(listingEntry)
}
}
class ListingEntry : UIView {
var listingId: String?
var containerView: UIView!
init(listingId: String) {
super.init(frame: CGRect.zero)
self.listingId = listingId
self.setUpContainerView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
func setUpContainerView() {
containerView = UIView()
containerView.backgroundColor = .gray
self.addSubview(containerView)
containerView.snp.makeConstraints { (make) in
// you want the "listing container" to be 150 x 150 pts
make.width.equalTo(150)
make.height.equalTo(150)
// and it needs top and bottom constraints to give self a height value
make.top.bottom.equalToSuperview()
// and it needs an x-position constraint
make.leading.equalToSuperview()
}
}
}
}
I've set the "main" ListingsView background color to red ... you don't see it because its containerView subview is green and fills the view.
Each ListingEntry view has a cyan background color, and its containerView has a gray background color.
The result:
and Debug View Hierarchy:
Last notes...
You set your StackView .distribution = .equalSpacing but you also set .spacing = 10, which doesn't make sense.
If you have more ListingEntry views than will fit vertically, you'll run into problems. I'd expect you'd put that into a scroll view.
I made a segmented control in my app and I want to show images instead of text.
So, I created UIImage array and then I added this array to segmented control items. These images looks like they are added but showing correctly.
What i have tried so far
import UIKit
class TestView: UIView {
var segmentedControls : UISegmentedControl = {
var items : [UIImage] = [UIImage]()
items=[UIImage(named:"pokeball-1")!,UIImage(named:"pokeball-1")!,UIImage(named:"pokeball-1")!]
var sc = UISegmentedControl(items: items)
sc.tintColor = .blue
sc.selectedSegmentIndex = 0
sc.translatesAutoresizingMaskIntoConstraints = false;
return sc
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupViews()
setupConstraints()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setupViews(){
addSubview(segmentedControls)
}
func setupConstraints(){
let h = self.frame.height
let w = self.frame.width
segmentedControls.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
segmentedControls.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
segmentedControls.widthAnchor.constraint(equalToConstant: 200).isActive = true
segmentedControls.heightAnchor.constraint(equalToConstant: 60).isActive = true
}
}
Output of my code
How can I show these images correctly? Please help me and thanks all of answers.
Segmented control images are template images. If you want to see the actual image, change the image rendering mode to .alwaysOriginal.
So I have a main viewController that has a stack view to which I want to add multiple instances of custom views I have created.
So my thought process was to create a custom view that inherits from UIView. I want the view to always be 40x40 so I created a new init to take care of this? (not sure if this is correct):
class QuickAddView: UIView {
#IBOutlet weak var iconLabel: UILabel!
var task: Task = Task()
public init(task: Task) {
self.task = task
super.init(frame: CGRect(x: 0, y: 0, width: 40, height: 40))
configureView()
}
private func configureView() {
self.layer.cornerRadius = self.frame.size.width / 2
self.backgroundColor = task.color
configureViewElements()
}
private func configureViewElements() {
configureIconLabel()
}
private func configureIconLabel() {
// CRASH: - iconLabel is nil here
self.iconLabel.text = task.icon
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
}
I then have a QuickAddView nib that sets its custom class to QuickAddView.swift
Lastly, I create the custom views in my viewController:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
configureViewElements()
}
private func configureViewElements() {
configureQuickAddStackView()
}
private func configureQuickAddStackView() {
let quickAddView = QuickAddView(task: Task(name: "Go to the store", icon: "🍐", color: .purple))
quickAddStackView.addArrangedSubview(quickAddView)
}
The problem I'm having is that my iconLabel is nil when I try to set up my QuickAddView. I also don't know if I'm doing this process of creating a custom view correct.
Since you are connecting the IBOutlet from xibFile you have to use
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
self.frame = CGRect(x: 0, y: 0, width: 40, height: 40)
configureView()
}
Also in your class you have to instantiate it with
if let quickAddView = Bundle.mainBundle().loadNibNamed("QuickAddView", owner: self, options: nil).first as? QuickAddView {
quickAddView.task = task
}
The problem is that you're creating a custom view in code, but using an IBOutlet for the iconLabel. An IBOutlet is for something you've built in Interface Builder, not in code. If you want to create the QuickAddView in code, you need to also create the iconLabel in code, in the QuickAddView class. You need to make these modifications:
weak var iconLabel: UILabel?
private func configureIconLabel() {
iconLabel = UILabel(frame: CGRect())
if let iconLabel = iconLabel {
iconLabel.text = task.icon
addSubview(iconLabel)
}
}
Note that I passed in a zeroed out CGRect, but you'll want to use whatever you want the UILabels origin and size to be, or use autolayout to configure where the iconLabel is displayed.