I want to display a little circle point as indicator to the selected UITabBarItem. How can i do this?
I use a custom UITabBarController. It looks like this:
import UIKit
class EventTabBar: UITabBarController {
override func awakeFromNib() {
tabBar.barTintColor = UIColor.white
tabBar.tintColor = UIColor(red: 79/255, green: 122/255, blue: 198/255, alpha: 1)
tabBar.unselectedItemTintColor = UIColor(red: 198/255, green: 203/255, blue: 209/255, alpha: 1)
tabBar.isTranslucent = false
tabBar.shadowImage = UIImage()
tabBar.backgroundImage = UIImage()
//Add Shadow to TabBar
tabBar.layer.shadowOpacity = 0.12
tabBar.layer.shadowOffset = CGSize(width: 0, height: 2)
tabBar.layer.shadowRadius = 8
tabBar.layer.shadowColor = UIColor.black.cgColor
tabBar.layer.masksToBounds = false
}
}
Can i use the selectionIndicatorImage to do this?
Hope you can help me. Thanks for your answer
let size = CGSize(width: tabController.tabBar.frame.width / (amount of items),
height: tabController.tabBar.frame.height)
let dotImage = UIImage().createSelectionIndicator(color: .blue, size: size, lineHeight: 7)
tabController.tabBar.selectionIndicatorImage = dotImage
-
extension UIImage {
func createSelectionIndicator(color: UIColor, size: CGSize, lineHeight: CGFloat) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
let innerRect = CGRect(x: (size.width/2) - lineHeight/2,
y: size.height - lineHeight - 2,
width: lineHeight,
height: lineHeight)
let path = UIBezierPath(roundedRect: innerRect, cornerRadius: lineHeight/2)
path.fill()
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
It's pretty easy actually. TabBarButton has two properties to set image, one is TabBarItem.image and another is TabBarItem.selectedImage set an image without the circle point for TabBarItem.image property and set an image with circle point for TabBarItem.selectedImage property.
If you want to set only the circle point for selected state, set the normal image property to UIImage(). Hope this solves the problem.
scanTabBarItem.image = UIImage.fontAwesomeIcon(name: .qrcode, textColor: .white, size: CGSize(width: 30, height: 30))
scanTabBarItem.selectedImage = UIImage.fontAwesomeIcon(name: .addressBook, textColor: .white, size: CGSize(width: 30, height: 30))
if not to show any image normally,
scanTabBarItem.image = UIImage()
I try change background color, when user tap on tab bar item. My code:
//MARK: func
func settingTabBar(){
//настроили бэкгроунд активного таб бара на зеленный цвет
let numberOfItems = CGFloat((tabBarController?.tabBar.items!.count)!)
let tabBarItemSize = CGSize(width: (tabBarController?.tabBar.frame.width)! / numberOfItems, height: (tabBarController?.tabBar.frame.height)!)
tabBarController?.tabBar.selectionIndicatorImage = UIImage.imageWithColor(color: Helpers.followGreenColor, size: tabBarItemSize).resizableImage(withCapInsets: .zero)
// remove default border
tabBarController?.tabBar.frame.size.width = self.view.frame.width + 4
tabBarController?.tabBar.frame.origin.x = -2
}
Select item color changes but not completely. Look screen below. THX.
upd:
I solved the problem with the following code
class ChildTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
changeBckgColorSelection()
}
fileprivate func changeBckgColorSelection() {
let imgSize = CGSize(width: tabBar.frame.size.width / CGFloat(tabBar.items!.count),
height: tabBar.frame.size.height)
UIGraphicsBeginImageContextWithOptions(imgSize, false, 0)
let p = UIBezierPath(rect: CGRect(x: 0, y: 0, width: imgSize.width,
height: imgSize.height))
Helpers.followGreenColor.setFill()
p.fill()
let finalImg = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
self.tabBar.selectionIndicatorImage = finalImg
}
}
i'm need add two vertical line on tabbar item. how i can make this? Thank.
add next code:
var imageLeft: UIImageView?
var imageRight: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
let grayColor = UIColor(red: 170/255, green: 170/255, blue: 170/255, alpha: 1.0)
let leftLine = (tabBar.frame.width/2) - ((tabBar.frame.width/5)/2)
let rightLine = (tabBar.frame.width/2) + ((tabBar.frame.width/5)/2)
imageLeft = UIImageView(image: createImage(color: grayColor, size: tabBar.frame.size, x: leftLine))
imageRight = UIImageView(image: createImage(color: grayColor, size: tabBar.frame.size, x: rightLine))
tabBar.addSubview(imageLeft!)
tabBar.addSubview(imageRight!)
}
func createImage(color: UIColor, size: CGSize, x: CGFloat) -> UIImage {
let rect: CGRect = CGRect(x: x, y: 5, width: 1, height: tabBar.frame.height - 11)
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
my result:
To do this you can subclass UITabBarController and add a UIImageView at the location you want it to be:
class TabBarController: UITabBarController {
var image: UIImageView?
override func viewDidLoad() {
super.viewDidLoad()
image = UIImageView(image: createImage(color: UIColor(red:0.18, green:0.66, blue:0.24, alpha:1.0), size: tabBarItemSize, lineHeight: 4))
tabBar.addSubview(image!)
}
func createImage(color: UIColor, size: CGSize, lineHeight: CGFloat) -> UIImage {
let rect: CGRect = CGRect(x: 0, y: size.height - lineHeight, width: size.width, height: lineHeight )
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}
}
You'll need to modify this for your needs and calculate the location you'd like it to be at, but this should help. I shine some more light on this in an article I wrote.
You need to use a custom tab bar.
Here is a tutorial on how do make one: https://github.com/codepath/ios_guides/wiki/Creating-a-Custom-Tab-Bar
Or you can find a library that will do it for you on Github
Right now, I have a custom graph class which creates bar charts and displays them. However they only appear after I appear on the view controller twice. For eg. here I am on one of the view controllers that has the graph but it shows in an incomplete manner.
Then here I am at the same view controller but with the graph how it is meant to look like. As you can see, the bars have gotten longer and the dashed lines have appeared.
To achieve this, all I did was click on one of the tabs in the bottom and then clicked back on the tab for report and it changed the view. I really have no idea why this is happening and have been trying to tackle this for 2 days now. If someone can help me it would be great. Here is the code for a sample viewcontroller that shows the graph.
override func viewDidLoad() {
self.tabBarController?.tabBar.barTintColor = UIColor(red:0.18, green:0.21, blue:0.25, alpha:1.0)
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func viewWillAppear(_ animated: Bool) {
self.tabBarController?.tabBar.barTintColor = UIColor(red:0.18, green:0.21, blue:0.25, alpha:1.0)
tupleArray.removeAll()
newTuple.removeAll()
filteredCategories.removeAll()
categories.removeAll()
self.categories = CoreDataHelper.retrieveCategories()
tableView.tableFooterView = UIView(frame: .zero)
tableView.tableFooterView?.isHidden = true
tableView.backgroundColor = UIColor.clear
tableView.layoutMargins = UIEdgeInsets.zero
tableView.separatorInset = UIEdgeInsets.zero
tableView.separatorStyle = UITableViewCellSeparatorStyle.singleLine
UIColor(red:0.40, green:0.43, blue:0.48, alpha:1.0)
currentYear1 = Calendar.current.date(byAdding: .year, value: -1, to: Date())!
currentYear = String(describing: Calendar.current.component(.year, from: currentYear1))
yearLabel.text = "\(currentYear)"
if ExpensesAdditions().yearHasExpense(year: currentYear){
noExpenseLabel.isHidden = true
}
else{
tableView.isHidden = true
barChartView.isHidden = true
noExpenseLabel.isHidden = false
totalSpentLabel.isHidden = true
}
totalSpent = ExpensesAdditions().retrieveYearlySum(year:currentYear)
totalSpentLabel.text = "Total Spent: " + ExpensesAdditions().convertToMoney(totalSpent)
for category in categories{
if ExpensesAdditions().categoryinYearHasExpense(year:currentYear,category:category.title!){
filteredCategories.append(category)
tupleArray.append((category.title!,ExpensesAdditions().retrieveCategoryExpenseForYear(category: category.title!, year: currentYear)))
}
else{}
}
newTuple = tupleArray.sorted(by: { $0.1 > $1.1 })
if ExpensesAdditions().yearHasExpense(year: currentYear){
let dataEntries = generateDataEntries()
barChartView.dataEntries = dataEntries
}
}
override func viewDidAppear(_ animated: Bool) {
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return filteredCategories.count
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "lastYearAnalysisCell") as! LastYearAnalysisViewTableViewCell
cell.isUserInteractionEnabled = false
let row = indexPath.row
let percentage:Double = (newTuple[row].1/totalSpent)*100
let percentageDisplay:Int = Int(percentage.rounded())
cell.nameLabel.text = newTuple[row].0
cell.amountLabel.text = "(\(percentageDisplay)"+"%) "+String(describing: UserDefaults.standard.value(forKey: "chosenCurrencySymbol")!)+ExpensesAdditions().convertToMoney(newTuple[row].1)
return cell
}
func generateDataEntries() -> [BarEntry] {
let colors = [#colorLiteral(red: 0.4666666687, green: 0.7647058964, blue: 0.2666666806, alpha: 1), #colorLiteral(red: 0.2392156869, green: 0.6745098233, blue: 0.9686274529, alpha: 1), #colorLiteral(red: 0.9607843161, green: 0.7058823705, blue: 0.200000003, alpha: 1), #colorLiteral(red: 0.9372549057, green: 0.3490196168, blue: 0.1921568662, alpha: 1), #colorLiteral(red: 0.8078431487, green: 0.02745098062, blue: 0.3333333433, alpha: 1), #colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1)]
maximum = newTuple[0].1
var result: [BarEntry] = []
/// ---- Complicated Divising -------//////
var maximumString:String = ExpensesAdditions().convertToMoney(maximum)
var maximumInt:Int = Int(maximumString.getAcronyms())!
let endIndex = maximumString.index(maximumString.endIndex, offsetBy: -3)
let truncated = maximumString.substring(to: endIndex)
if maximumInt < 5{
divisor = Double((5*(pow(10, truncated.count - 1))) as NSNumber)
}
else if maximumInt > 5{
divisor = Double((pow(10, truncated.count)) as NSNumber)
}
for i in 0..<filteredCategories.count {
let value = ExpensesAdditions().convertToMoney(tupleArray[i].1)
var height:Double = Double(value)! / divisor
result.append(BarEntry(color: colors[i % colors.count], height: height, textValue: value, title: filteredCategories[i].title!))
}
return result
//// --- Complicated Divising End -------- /////
}
If you need anymore code, please let me know and thanks in advance!
Here is the code for the bar chart entry code:
import UIKit
class BasicBarChart: UIView {
/// the width of each bar
let barWidth: CGFloat = 40.0
/// space between each bar
let space: CGFloat = 20.0
/// space at the bottom of the bar to show the title
private let bottomSpace: CGFloat = 40.0
/// space at the top of each bar to show the value
private let topSpace: CGFloat = 40.0
/// contain all layers of the chart
private let mainLayer: CALayer = CALayer()
/// contain mainLayer to support scrolling
private let scrollView: UIScrollView = UIScrollView()
var dataEntries: [BarEntry]? = nil {
didSet {
mainLayer.sublayers?.forEach({$0.removeFromSuperlayer()})
if let dataEntries = dataEntries {
scrollView.contentSize = CGSize(width: (barWidth + space)*CGFloat(dataEntries.count), height: self.frame.size.height)
mainLayer.frame = CGRect(x: 0, y: 0, width: scrollView.contentSize.width, height: scrollView.contentSize.height)
drawHorizontalLines()
for i in 0..<dataEntries.count {
showEntry(index: i, entry: dataEntries[i])
}
}
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
convenience init() {
self.init(frame: CGRect.zero)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
private func setupView() {
scrollView.layer.addSublayer(mainLayer)
self.addSubview(scrollView)
}
override func layoutSubviews() {
scrollView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
}
private func showEntry(index: Int, entry: BarEntry) {
/// Starting x postion of the bar
let xPos: CGFloat = space + CGFloat(index) * (barWidth + space)
/// Starting y postion of the bar
let yPos: CGFloat = translateHeightValueToYPosition(value: Float(entry.height))
drawBar(xPos: xPos, yPos: yPos, color: entry.color)
/// Draw text above the bar
drawTextValue(xPos: xPos - space/2, yPos: yPos - 30, textValue: entry.textValue, color: entry.color)
/// Draw text below the bar
drawTitle(xPos: xPos - space/2, yPos: mainLayer.frame.height - bottomSpace + 10, title: entry.title, color: entry.color)
}
private func drawBar(xPos: CGFloat, yPos: CGFloat, color: UIColor) {
let barLayer = CALayer()
barLayer.frame = CGRect(x: xPos, y: yPos, width: barWidth, height: mainLayer.frame.height - bottomSpace - yPos)
barLayer.backgroundColor = color.cgColor
mainLayer.addSublayer(barLayer)
}
private func drawHorizontalLines() {
self.layer.sublayers?.forEach({
if $0 is CAShapeLayer {
$0.removeFromSuperlayer()
}
})
let horizontalLineInfos = [["value": Float(0.0), "dashed": true], ["value": Float(0.5), "dashed": true], ["value": Float(1.0), "dashed": true]]
for lineInfo in horizontalLineInfos {
let xPos = CGFloat(0.0)
let yPos = translateHeightValueToYPosition(value: (lineInfo["value"] as! Float))
let path = UIBezierPath()
path.move(to: CGPoint(x: xPos, y: yPos))
path.addLine(to: CGPoint(x: scrollView.frame.size.width, y: yPos))
let lineLayer = CAShapeLayer()
lineLayer.path = path.cgPath
lineLayer.lineWidth = 0.5
if lineInfo["dashed"] as! Bool {
lineLayer.lineDashPattern = [4, 4]
}
lineLayer.strokeColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1).cgColor
self.layer.insertSublayer(lineLayer, at: 0)
}
}
private func drawTextValue(xPos: CGFloat, yPos: CGFloat, textValue: String, color: UIColor) {
let textLayer = CATextLayer()
textLayer.frame = CGRect(x: xPos, y: yPos, width: barWidth+space, height: 22)
textLayer.foregroundColor = color.cgColor
textLayer.backgroundColor = UIColor.clear.cgColor
textLayer.alignmentMode = kCAAlignmentCenter
textLayer.contentsScale = UIScreen.main.scale
textLayer.font = CTFontCreateWithName(UIFont.systemFont(ofSize: 0).fontName as CFString, 0, nil)
textLayer.fontSize = 14
textLayer.string = textValue
mainLayer.addSublayer(textLayer)
}
private func drawTitle(xPos: CGFloat, yPos: CGFloat, title: String, color: UIColor) {
let textLayer = CATextLayer()
textLayer.frame = CGRect(x: xPos, y: yPos, width: barWidth + space, height: 22)
textLayer.foregroundColor = color.cgColor
textLayer.backgroundColor = UIColor.clear.cgColor
textLayer.alignmentMode = kCAAlignmentCenter
textLayer.contentsScale = UIScreen.main.scale
textLayer.font = CTFontCreateWithName(UIFont.systemFont(ofSize: 0).fontName as CFString, 0, nil)
textLayer.fontSize = 14
textLayer.string = title
mainLayer.addSublayer(textLayer)
}
private func translateHeightValueToYPosition(value: Float) -> CGFloat {
let height: CGFloat = CGFloat(value) * (mainLayer.frame.height - bottomSpace - topSpace)
return mainLayer.frame.height - bottomSpace - height
}
}
The problem is most likely that you set up your bar chart in viewWillAppear, at which point the final size of the view has not been set yet. To fix this you must either delay setting the data until in viewDidAppear, or you need to trigger a redraw of the bars and dashed lines when the frame size has changed i.e. from layoutSubviews.
One point of advice I want to share with you is that you should try to separate your code so that sizing and positioning of all the view's sub-items are done in layoutSubviews, and the actual adding and removal of them are done elsewhere.
Update:
In your class BasicBarChart you should try to separate adding the chart elements, and do the actual sizing of the elements. This is because sizing is an event that often occurs more than once, especially when using auto-layout.
If your BasicBarChart view is set up with constraints, it will usually receive at least two different sizes during setup, once with the sizes in your storyboard file, and once with the actual device derived sizes. Since you don't want to add everything more than once, you should do the adding elsewhere. A good place could be during initialization or when setting data.
The sizing of a view's sub-elements is best done in its layoutSubviews() function. You have already started! Just move over the sizing of your other elements here too, and everything should work much better.
class BasicBarChart: UIView {
override func layoutSubviews() {
scrollView.frame = CGRect(x: 0, y: 0, width: self.frame.size.width, height: self.frame.size.height)
// Size and position bars, labels and dashed lines here
}
}
I am trying to use custom color in MFMessageComposeViewController. I thought it will be okay to use:
#IBAction func sendMessage(sender: AnyObject) {
let messageVC = MFMessageComposeViewController()
messageVC.body = "Enter a message";
messageVC.recipients = [phoneString]
messageVC.messageComposeDelegate = self;
//color
UINavigationBar.appearance().barTintColor = UIColor(hexString: "fec13e")
UINavigationBar.appearance().tintColor = UIColor.whiteColor()
self.presentViewController(messageVC, animated: false, completion: nil)
}
but its not working.
You can use the below code to change the color of MFMessageComposeViewController navigation color.
let size = CGSize(width: (self.window?.frame.size.width)!, height: 64)
let image = self.getImageWithColor(UIColor .init(colorLiteralRed: 40.0/255.0, green: 140.0/255.0, blue: 204.0/255.0, alpha: 1.0), size:size)
UINavigationBar.appearance().setBackgroundImage(image.resizableImage(withCapInsets: UIEdgeInsets.zero, resizingMode: .stretch), for: .default)
Where getImageWithColor is the func which return image of desired color
func getImageWithColor(_ color: UIColor, size: CGSize) -> UIImage
{
let rect = CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: size.width, height: size.height))
UIGraphicsBeginImageContextWithOptions(size, false, 0)
color.setFill()
UIRectFill(rect)
let image: UIImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return image
}