A view instead of a Tableview when no item in Swift - ios

I would like to do something similar to app Clock.
I have a tableview which is loaded dynamically.
I would like to add a view with a text message instead of the empty tableview when no item is available.
I used actionSheet, to indicate when no item is available, it works, but I would much prefer to show a view with a text message as it is in app Clock.
Do you have any idea how to proceed?
override func viewWillAppear(animated: Bool) {
// get the destinations data ...
if (destinations.count<1){
var myActionSheet: UIActionSheet = UIActionSheet()
let title: String = NSLocalizedString("DestinationsMsg", comment: "")
myActionSheet.title = title
myActionSheet.delegate = self;
myActionSheet.addButtonWithTitle("Cancel")
myActionSheet.addButtonWithTitle("Add a Destination")
myActionSheet.cancelButtonIndex = 0
myActionSheet.showInView(self.view)
}
super.viewWillAppear(true);
}
This is the solution, even I am not completely satisfied with the position of the label.
else {
var bounds: CGRect = UIScreen.mainScreen().bounds
var width:CGFloat = bounds.size.width
var height:CGFloat = bounds.size.height
var view1 = UIView()
let label = UILabel(frame: CGRect(x: 0, y: height / 4, width: width, height: 40))
label.textColor = UIColor(red: 160/255, green: 160/255, blue: 160/255, alpha: 1.0)
label.font = UIFont.boldSystemFontOfSize(26.0)
label.textAlignment = NSTextAlignment.Center
label.text = "No Destination"
view1.addSubview(label)
tableView.tableHeaderView = view1
}

Set the table view's tableHeaderView to your placeholder view when there are no rows to display. Set tableHeaderView to nil when there are rows to display.

Related

Swift & UILabel : How to add padding and margin in Swift programmatically? [duplicate]

This question already has answers here:
Add padding between label and its border
(4 answers)
Closed 8 months ago.
I have created a text programmatically with a grey background using UILabel.
Now I would like to add padding to this paragraph/text. Also, it would be great if you could show me how to add margin to my UILabel as well.
import UIKit
final class SignUpViewController: UIViewController {
public let identifier = "Sign Up"
private let logoImage : UIImageView = {
let imageView = UIImageView()
imageView.layer.masksToBounds = true
imageView.contentMode = .scaleAspectFit
imageView.image = UIImage(named: "MyLogoWithTitle")
imageView.clipsToBounds = true
return imageView
}()
private let instructionText : UILabel = {
let label = UILabel()
label.text = "Please read terms and conditions below carefully before proceeding with the registration."
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
label.numberOfLines = 0
label.tintColor = .white
return label
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
view.addSubview(logoImage)
view.addSubview(instructionText)
view.backgroundColor = UIColor().colorFromHex(hex: "#141920", opacity: 1.0)
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
logoImage.frame = CGRect(x: 0,
y: 0,
width: 140,
height: 60)
logoImage.center = CGPoint(x: view.center.x, y: view.height/5)
instructionText.frame = CGRect(
x: 5,
y: 5 + logoImage.bottom,
width: view.width - 20,
height: 50)
.integral
instructionText.layer.cornerRadius = 10
}
}
Notice that I created an extension to UIColor so that I can input hex color in this way - UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4) .
I am looking forward to hearing from you. Thank you.
You can insert this UILabel into the container (any UIView) and set its position inside.
But the simplest trick is to use UIButton instead of UILabel. You can configure UIEdgeInsets for padding.
So that UIButton does not act as a button simply set button.isUserInteractionEnabled = false.
By default, text in the button are placed in the center, but its position is easy to change with contentHorizontalAlignment and contentVerticalAlignment
And as a bonus, you can add icons right near to the text. :)
UPD.
Could you give me a simple example? I tried that way but I didn't get the result I expected. – Punreach Rany
let buttonUsedAsLabel = UIButton()
// Your question was about padding
// It's it!
buttonUsedAsLabel.titleEdgeInsets = UIEdgeInsets(top: 5, left: 20, bottom: 5, right: 20)
// Make it not user interactable as UILabel is
buttonUsedAsLabel.isUserInteractionEnabled = false
// set any other properties
buttonUsedAsLabel.setTitleColor(.white, for: .normal)
buttonUsedAsLabel.contentVerticalAlignment = .top
buttonUsedAsLabel.contentHorizontalAlignment = .left
// Set title propeties AFTER it was created with text because it's nullable
// You can use attributed title also
// Never use any (button.titleLabel) before its creation
// for example: (button.titleLabel?.text = "zzz") do nothing here
buttonUsedAsLabel.setTitle("This is the text", for: .normal)
buttonUsedAsLabel.titleLabel?.font = .systemFont(ofSize: 20, weight: .medium)
buttonUsedAsLabel.titleLabel?.numberOfLines = 0
buttonUsedAsLabel.titleLabel?.lineBreakMode = .byWordWrapping
// and so on
// ...
// This is the triсk :)
Of course, you can do it with a storyboard if prefer.
1. Add this class
PaddingLabel.swift
import UIKit
class PaddingLabel: UILabel {
var edgeInset: UIEdgeInsets = .zero
override func drawText(in rect: CGRect) {
let insets = UIEdgeInsets.init(top: edgeInset.top, left: edgeInset.left, bottom: edgeInset.bottom, right: edgeInset.right)
super.drawText(in: rect.inset(by: insets))
}
override var intrinsicContentSize: CGSize {
let size = super.intrinsicContentSize
return CGSize(width: size.width + edgeInset.left + edgeInset.right, height: size.height + edgeInset.top + edgeInset.bottom)
}
}
2. Add this code to your ViewController
let label = PaddingLabel()
override func viewDidLoad() {
super.viewDidLoad()
label.backgroundColor = UIColor().colorFromHex(hex: "#2C333C", opacity: 0.4)
//Setting the padding label
label.edgeInset = UIEdgeInsets(top: 5, left: 10, bottom: 5, right: 10)
}
The answer to the link below is that I wrote the same content based on the storyboard.
Add padding between label and its border
I use textfield. Set padding and text in textfield. And do not allow editing.
extension UITextField {
func addLeftPadding() {
let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: self.frame.height))
self.leftView = paddingView
self.leftViewMode = ViewMode.always
}
}
//ViewController
#IBOutlet weak var myTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
myTextField.addLeftPadding()
myTextField.isUserInteractionEnabled = false
myTextField.text = "your label text"
}

Make custom title view click able swift

I customise the title view add a image and labels in it know I want to make it clickable but its not working. I added tap gesture in title view but its not working.
here is my code.
func navTitleWithImageAndText(titleText: String,subTitleText:String, imageName: String) -> UIView {
// Creates a new UIView
let titleView = UIView()
let label = UILabel(frame: CGRect(x: 0, y: -15, width: 0, height: 0))
// Creates a new text label
// let label = UILabel()
label.text = titleText
label.font = UIFont.boldSystemFont(ofSize: 17)
//label.center = titleView.center
label.textColor = .white
//label.textAlignment = NSTextAlignment.center
label.sizeToFit()
let subtitleLabel = UILabel(frame: CGRect(x: 0, y: 5, width: 0, height: 0))
subtitleLabel.backgroundColor = UIColor.clear
subtitleLabel.textColor = UIColor.white
subtitleLabel.font = UIFont.systemFont(ofSize: 12)
subtitleLabel.text = subTitleText
subtitleLabel.sizeToFit()
// Creates the image view
let image = UIImageView()
image.image = UIImage(named: imageName)
// Maintains the image's aspect ratio:
let imageAspect = image.image!.size.width / image.image!.size.height
// Sets the image frame so that it's immediately before the text:
let imageX = label.frame.origin.x - label.frame.size.height * imageAspect - 20
let imageY = label.frame.origin.y
let imageWidth = label.frame.size.height * imageAspect + 15
let imageHeight = label.frame.size.height + 15
image.frame = CGRect(x: imageX, y: imageY, width: imageWidth, height: imageHeight)
image.contentMode = UIView.ContentMode.scaleAspectFit
// Adds both the label and image view to the titleView
titleView.addSubview(label)
titleView.addSubview(image)
titleView.addSubview(subtitleLabel)
// Sets the titleView frame to fit within the UINavigation Title
titleView.sizeToFit()
return titleView
}
add tap gesture in it. but why its not working.
titleView.addGestureRecognizer(titleGestureRecognizer)
I have implemented a tap gesture on a label in the following way:-
Initialize Variable
var tap_gest = UITapGestureRecognizer()
Add gesture on Label. Add the following code in "viewDidLoad()"
if normal label then use below
self.register_as_company_lbl.isUserInteractionEnabled = true // register_as_company_lbl is my label where tap gesture added
self.tap_gest.addTarget(self, action: #selector(registerAsComapany))
self.register_as_company_lbl.addGestureRecognizer(tap_gest)
if on navigation bar title then use below
self.navigationController?.navigationBar.addGestureRecognizer(tap_gest)
Define Selector method in your ViewController Class as following
#objc func registerAsComapany(){//Add your task what you perform, In my case I have to push to another screen
let vc = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "CompanyDetailsViewController") as! CompanyDetailsViewController
self.navigationController?.pushViewController(vc , animated: true)
}
titleView.isUserInteractionEnabled = true

Why setting titleView lowers header text?

I'm trying to dynamically set my navigation bar's text so that the header text always fits. I'm acccomplishing that like this:
// Pet's Day text "Joy's Day"
if let range = currentPet.range(of: "_") {
let petsName = currentPet[range.upperBound..<currentPet.endIndex]
let deviceWidth = UIScreen.main.bounds.size.width
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: deviceWidth, height: 40))
titleLabel.text = "\(petsName)'s Day"
titleLabel.font = UIFont.systemFont(ofSize: 30)
titleLabel.backgroundColor = UIColor.clear
titleLabel.textColor = UIColor.white
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.5
titleLabel.textAlignment = .center
self.navBar.topItem?.titleView = titleLabel
}
However, as seen by this picture, this lowers the header text below its natural height:
The navigation bar on the left is from one of my app's other views, while the one on the right is the one I'm setting.
Both of these navigation bars are navigation bars that I've dragged in and made the prompt equal to an empty string to increase their height:
Can anybody please help me implement my code above so that it doesn't drop down the header text?
**Edit: Here are screenshots from Xcode's debug hierarchy:
This is the normal navigation bar:
This is the one I'm setting:
You need to set the baseline, by default is Align Baseline, you need to change to Align Centers
Code example:
class TestViewController: UIViewController {
#IBOutlet weak var navBar: UINavigationBar!
override func viewDidLoad() {
super.viewDidLoad()
// Change the height of the navbar
self.navBar.frame = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: 68)
let deviceWidth = UIScreen.main.bounds.size.width
let titleLabel = UILabel(frame: CGRect(x: 0, y: 0, width: deviceWidth, height: 40))
titleLabel.text = "Log Events" // Change to Joy's Day and check the result
titleLabel.font = UIFont.systemFont(ofSize: 30)
titleLabel.baselineAdjustment = .alignBaselines
titleLabel.textColor = UIColor.white
titleLabel.adjustsFontSizeToFitWidth = true
titleLabel.minimumScaleFactor = 0.5
titleLabel.textAlignment = .center
self.navBar.topItem?.titleView = titleLabel
}
}

Swift - Customize view to show over an empty table cells

I am using Swift 3, Xcode 8.2.
I've been able to create a label to cover the empty table view cells when there are none to display.
My code is below and it is located in the subclass of UITableViewController.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
tableView.backgroundView = noScanLabel
tableView.separatorStyle = .none
return 0
}
}
Here is the result.
Looks fine. But how, do I make it such that I include another line of text with a downward arrow pointing at the raised center button. Something like "Click here to start a scan"?
I've tried adding new line characters to the noScanLabel.text field but that didn't work out. Any pointers in the right direction would be helpful.
The simple solution is to set numberOfLines to 0 on noScanLabel. This way, the new lines will show.
let noScanLabel: UILabel = UILabel(frame: rect)
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.numberOfLines = 0
noScanLabel.textAlignment = NSTextAlignment.center
Note than in such cases, I would recommend, for better maintainability, to actually remove the TableView from the UIViewController (hence not inherit from UITableViewController) and replace it with an empty view when you detect no scans are available. This will make each state more independent of each other and make maintenance easier.
There are a few ways achieve your goal. There is a well known library called DZNEmptyDataSet for handling empty tableviews and collectionviews . https://github.com/dzenbot/DZNEmptyDataSet
The other way would be to create a uiview with your specified rect and then add two labels to that uiview. One would be your noScanLabel and the other would be a label or image containing your arrow. You can set the layout constraints as required so that the arrow appears pointing down.
This code seems to work well. Change constraints if needed
let rect = CGRect(x: 0, y: 0, width: tableview.bounds.size.width, height: tableview.bounds.size.height)
let noDataView = UIView(frame: rect)
let noScanLabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
let arrowLabel = UILabel()
arrowLabel.text = "Add Arrow Image to this label"
arrowLabel.textColor = UIColor.gray
arrowLabel.font = UIFont.boldSystemFont(ofSize: 24)
arrowLabel.textAlignment = NSTextAlignment.center
noScanLabel.widthAnchor.constraint(equalToConstant: 100)
arrowLabel.widthAnchor.constraint(equalToConstant: 50)
noDataView.addSubview(noScanLabel)
noDataView.addSubview(arrowLabel)
arrowLabel.translatesAutoresizingMaskIntoConstraints = false
noDataView.translatesAutoresizingMaskIntoConstraints = false
noScanLabel.translatesAutoresizingMaskIntoConstraints = false
self.tableview.addSubview(noDataView)
noDataView.isHidden = false
noDataView.centerXAnchor.constraint(equalTo: self.tableview.centerXAnchor).isActive = true
noDataView.centerYAnchor.constraint(equalTo: self.tableview.centerYAnchor).isActive = true
noScanLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
noScanLabel.topAnchor.constraint(equalTo: noDataView.centerYAnchor).isActive = true
arrowLabel.centerXAnchor.constraint(equalTo: noDataView.centerXAnchor).isActive = true
arrowLabel.topAnchor.constraint(equalTo: noScanLabel.bottomAnchor).isActive = true
The other option is to set number of lines to zero as mentioned already
noScanLabel.numberLines = 0
You can take UIView and add your all UILabel and arrow Image on UIView and then assign that UIView to backgroundView of TableView.
Like this.
override func numberOfSections(in tableView: UITableView) -> Int {
// if there are scans to display...
if items.count > 0 {
tableView.backgroundView = nil
tableView.separatorStyle = .singleLine
return 1
}
else { // otherwise, return 0, remove cell lines, and display a Label
let rect = CGRect(x: 0,
y: 0,
width: tableView.bounds.size.width,
height: tableView.bounds.size.height)
let messageBaseView = UIView(frame: rect)
//Add your first label..
let noScanLabel: UILabel = UILabel()
noScanLabel.text = "No Scans"
noScanLabel.textColor = UIColor.gray
noScanLabel.font = UIFont.boldSystemFont(ofSize: 24)
noScanLabel.textAlignment = NSTextAlignment.center
messageBaseView.addSubView(noScanLabel)
//Add your second label.. and your arrow image here on messageBaseView
//Assign messageBaseView to backgroundView of tableView
tableView.backgroundView = messageBaseView
tableView.separatorStyle = .none
return 0
}
}

Customize navigation bar by adding two labels instead of title in Swift

I am trying to add two labels in the place where the title is shown in navigation bar, but I am struggling to do so. It would be very nice if I could achieve this with storyboard but as I can see I cannot do it.
As I have seen I need to use navigationItem but I do not know how exactly to do that. If anyone have any example or if anyone could explain me more specifically how to do so would be wonderful.
And I need to mention that I am completely unfamiliar with Obj-C, so any help would need to be in Swift.
I am not sure if you can do it from the storyboard, but if you want to add two title labels, you can do the following in the viewDidLoad() method of the view controller for which you want the two titles:
if let navigationBar = self.navigationController?.navigationBar {
let firstFrame = CGRect(x: 0, y: 0, width: navigationBar.frame.width/2, height: navigationBar.frame.height)
let secondFrame = CGRect(x: navigationBar.frame.width/2, y: 0, width: navigationBar.frame.width/2, height: navigationBar.frame.height)
let firstLabel = UILabel(frame: firstFrame)
firstLabel.text = "First"
let secondLabel = UILabel(frame: secondFrame)
secondLabel.text = "Second"
navigationBar.addSubview(firstLabel)
navigationBar.addSubview(secondLabel)
}
In this way you can add as many subviews as you want to the navigation bar
Here's an implementation that uses a stack view instead, which also gives you some versatility with layout of the labels:
class ViewController: UIViewController {
lazy var titleStackView: UIStackView = {
let titleLabel = UILabel()
titleLabel.textAlignment = .center
titleLabel.text = "Title"
let subtitleLabel = UILabel()
subtitleLabel.textAlignment = .center
subtitleLabel.text = "Subtitle"
let stackView = UIStackView(arrangedSubviews: [titleLabel, subtitleLabel])
stackView.axis = .vertical
return stackView
}()
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.titleView = titleStackView
}
override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
if view.traitCollection.horizontalSizeClass == .compact {
titleStackView.axis = .vertical
titleStackView.spacing = UIStackView.spacingUseDefault
} else {
titleStackView.axis = .horizontal
titleStackView.spacing = 20.0
}
}
}

Resources