I would like to do something in the vein of having a box appear or a circle around the tab bar item that the user has selected other than just changing the color. Is this even possible and if so how? Thanks in advance.
There are various ways to achieve this. I will list down 3 of them below.
1) You can set selectionIndicatorImage like this in AppDelegate,
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UITabBar.appearance().selectionIndicatorImage = #imageLiteral(resourceName: "boxImage")
return true
}
For the rest of 2 you have to create a subclass of UITabBarController like below and set this class in the storyboard for the tabBarController
class MyTabBarViewController: UITabBarController {
}
2) In this method you have to ask your designer to create the selected images with box. Set the unselected/normal and selected images like below in viewDidAppear and you are done.
3) Add an imageView in tabBar like below. In storyboard, you can assign tag for each tabBar item so that in the didSelect item callback, we will consider the tag as index of the selected item. Lets consider you have 5 tabBar items and you assigned tags from 0 to 4. Now you will just get the tag and update the position as shown below
class MyTabBarViewController: UITabBarController {
// MARK: - Properties
let imageView = UIImageView(image: #imageLiteral(resourceName: "boxImage"))
// MARK: View's Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
self.tabBar.addSubview(imageView)
}
/// Tabbar item selection callback
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
updateImagePosition(at: item.tag + 1)
}
private func updateImagePosition(at index: Int) {
guard let count = tabBar.items?.count else { return }
let eachItemWidth = view.bounds.width/CGFloat(count)
let selectedItemX = (eachItemWidth * CGFloat(index)) - eachItemWidth/2 - imageView.frame.width/2
let selectedItemY = tabBar.bounds.height/2 - imageView.frame.height/2
imageView.frame.origin = CGPoint(x: selectedItemX, y: selectedItemY)
}
}
Related
I'm somewhat new at developing iOS applications, I'm creating an app but I'm not using any storyboards.
I want to use a UITabBar, but when I click (or tap) any item of the bar, the delegate didSelect method is not being called.
I don't know what I am doing wrong, this is my code:
class MainMenuVC: BaseVC, UITabBarDelegate {
var tabBarMenu : UITabBar?
override func viewDidLoad() {
super.viewDidLoad()
self.tabBarMenu = UITabBar.init()
self.tabBarMenu?.delegate = self;
tabBarMenu!.items = barItems // I omitted the code to populate this array on purpose
// Tab Style
tabBarMenu!.backgroundColor = Constants.Colors.gray
tabBarMenu!.tintColor = Constants.Colors.gray
tabBarMenu!.barTintColor = Constants.Colors.gray
tabBarMenu!.isTranslucent = false
tabBarMenu!.isUserInteractionEnabled = true
//Tab bar position
tabBarMenu!.frame = CGRect(x: contentView.frame.origin.x, y: contentView.frame.size.height - Constants.Dimensions.tabBarHeight, width: contentView.frame.size.width, height: Constants.Dimensions.tabBarHeight)
// Adding
self.view.addSubview(tabBarMenu!)
self.view.bringSubviewToFront(tabBarMenu!)
}
// This is not being called
func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
self.handleTabBarTab(tag: item.tag)
}
}
My BaseVC is a regular UIViewController, I'm not using UITabBarViewController
Make sure to have an TAG for UITabBarItem, this works fine for me:
self.tabBarMenu = UITabBar(frame: CGRect(x: contentView.frame.origin.x, y: contentView.frame.size.height - Constants.Dimensions.tabBarHeight, width: contentView.frame.size.width, height: Constants.Dimensions.tabBarHeight))
self.tabBarMenu?.delegate = self
let tabOneBarItem = UITabBarItem(title: "Test", image: nil, tag: 1)
let tabTwoBarItem = UITabBarItem(title: "Test2", image: nil, tag: 2)
tabBarMenu!.items = [tabOneBarItem, tabTwoBarItem]
The code I posted here is a bit different from the code I'm actually working on.
What was actually causing trouble were some super UIViews where I was adding all the other views, including the UITabBar. These views were not enabled to user interaction.
So all I did was:
// Enabled the main view container to user interaction
// This was actually causing the issue
self.containerView.isUserInteractionEnabled = true
// Adding the views
self.containerView.addSubview(tabBarMenu!)
self.containerView.bringSubviewToFront(tabBarMenu!)
Everything worked good after that.
So this approach is actually functional
I am using Eureka to implement a simple form in my project. The form seems to have blue tint in the tint bar buttons above the keyboard and I am unsure how to change it.
Is there a way to make the "Done" label green like the rest of my UI?
Updated Eureka 4.3.x
You should override a variable customNavigationAccessoryView in your controller
override var customNavigationAccessoryView: (UIView & NavigationAccessory)? {
// The width might not be correctly defined yet: (Normally you can use UIScreen.main.bounds)
let navView = NavigationAccessoryView(frame: CGRect(x: 0, y: 0, width: view.frame.width, height: 44.0))
navView.barTintColor = UIColor.MaterialColors.category3
navView.tintColor = UIColor.white
return navView
}
Here you go and fix the colour of Done Label.
override NaviagtionAccessoryView Instance as navigationAccessoryView below
navigationAccessoryView.tintColor = "Your Colour"
navigationAccessoryView.backgroundColor = "Your Colour"
navigationAccessoryView.doneButton.tintColor = "Your Colour"
More Here
You can set a default tint color for all UIView instance by
UIView.appearance().tintColor = .red
appearance() is a method in UIAppearance protocol. UIView and its subclasses confirm to this protocol. You can also do
UIButton.appearance().tintColor = .green
Add the below line in AppDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{
self.window?.tintColor = UIColor.black // You can set it to which color you want.
return true
}
All the tint color of the whole app will be changed to the given color.
This works for me perfectly:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
NavigationAccessoryView.appearance().tintColor = /* your color here */
}
I have Tab Bar Controller with few tabs.
All tab items should have same non-selected color for image and text.
All tab items should have different selected color for image and
text.
I am ready to create images with colors needed for selected and non-selected items. And I know how to change this colors for all tabs from AppDelegate
UITabBar.appearance().tintColor = UIColorFromRGB(rgbValue: MY_COLOR, aplhaValue: 1.0)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColorFromRGB(rgbValue: MY_COLOR, aplhaValue: 1.0)], for: UIControlState.selected)
UITabBarItem.appearance().setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.white], for: UIControlState.normal)
But I want to make different colors for different items.
In your Storyboard select your TabBarController and give it a custom class: TabBarController in this example:
And then create the custom TabBarController file:
// Copyright © 2017 Erick Vavretchek. All rights reserved.
import UIKit
class TabBarController: UITabBarController {
enum tabBarMenu: Int {
case home
case list
case settings
}
// MARK: UITabBarController
override func viewDidLoad() {
super.viewDidLoad()
guard let tabBarMenuItem = tabBarMenu(rawValue: 0) else { return }
setTintColor(forMenuItem: tabBarMenuItem)
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
guard
let menuItemSelected = tabBar.items?.index(of: item),
let tabBarMenuItem = tabBarMenu(rawValue: menuItemSelected)
else { return }
setTintColor(forMenuItem: tabBarMenuItem)
}
// MARK: Private
private func setTintColor(forMenuItem tabBarMenuItem: tabBarMenu) {
switch tabBarMenuItem {
case .home:
viewControllers?[tabBarMenuItem.rawValue].tabBarController?.tabBar.tintColor = UIColor.green
case .list:
viewControllers?[tabBarMenuItem.rawValue].tabBarController?.tabBar.tintColor = UIColor.red
case .settings:
viewControllers?[tabBarMenuItem.rawValue].tabBarController?.tabBar.tintColor = UIColor.red
}
}
}
It is also important that in your Assets.xcassets folder you select the each image you are using for your TabBarItem and set them to Render As: Template Image. That's how you can simply change its tintColor to whatever you like:
I need a floating button in my iPhone app, I saw many libraries online like this one: https://cocoapods.org/pods/LiquidFloatingActionButton
But I need it to be just a single button, not a menu - Do you know of any other libraries that do this? Or a way to customise one of the current libraries to do what I need?
Thanks!
import UIKit
final class AppDelegate: NSObject, UIApplicationDelegate {
var window: UIWindow?
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject : AnyObject]?) -> Bool {
let buttonDiameter: CGFloat = 44.0
let floatingButton = UIButton(frame: CGRect(x: 0.0, y: 0.0, width: buttonDiameter, height: buttonDiameter))
floatingButton.layer.cornerRadius = buttonDiameter / 2.0
floatingButton.backgroundColor = .redColor()
window?.addSubview(floatingButton)
return true
}
}
This example would add a floating button to the top left corner of your app.
The important aspect of this code is that I am adding the button the UIWindow.
You can access the window of the app from any UIViewController whose view is added to the window via the property window.
To get notifications when a view controller is added to the window you can override the function:
didMoveToWindow()
There is an approach to this by using KCFloating action button and adding a tapGestureRecognizer to a view containing this button.
So, just adding KCFloatingActionButton's pod and adding another view containing it it's possible to have a single floating action button just as androids.
If you require more detail to solve this issue tell me and I'll try to help
Sample image
only copy and paste this code
override func viewDidAppear(_ animated: Bool) {
layoutFAB()
}
func layoutFAB() {
let item = KCFloatingActionButton()
item.buttonColor = UIColor(red: 188/255, green: 46/255, blue: 35/255, alpha: 1)
}
override func viewDidLoad() {
super.viewDidLoad()
let Fab = KCFloatingActionButton()
Fab.addItem("a", icon: UIImage(named: "a")){ item in
print("a")
}
}
self.view.addSubview(Fab)
}
I have a UITableView that contains UITextField in each cell
When I click UITextField in the cell, the keyboard will show and cover my cell. Therefore, I move my cell to the top by using.
- (void)keyboardWasShown:(NSNotification*)aNotification
{
CGPoint scrollPoint = CGPointMake(0, self.activeInputView.frame.origin.y);
[self.tableView setContentOffset:scrollPoint animated:YES];
}
If I use single click on each cell, my application work fine.
However, I use double click on each cell (it mean I tap on it 2 times very quickly), my cell will stop scroll to the top.
In this line
CGPoint scrollPoint = CGPointMake(0, self.activeInputView.frame.origin.y);
can be 2 errors.
Firstly, you have to make sure that inputView that you have captured is the view you want.
Secondly, you should convert fetched point to correct view using appropriate method.
See reference: https://developer.apple.com/library/ios/documentation/UIKit/Reference/UIView_Class/#//apple_ref/doc/uid/TP40006816-CH3-SW52
For further investigation need more context.
TPKeyboardAvoiding seems to be a solution.
Are you sure that the scrollPoint is correct?
I just created a sample project with a fixed offset and everything works perfectly fine. The table view scrolls upwards after single and double tap on the UITextField of a certain UITableViewCell.
- (void)keyboardDidShow:(NSNotification *)notification {
CGPoint point = CGPointMake(0.0, 30.0);
[self.tableView setContentOffset:point animated:YES];
}
rather then doing code for manually adjusting your tableview frame ,
Let me give you a suggestion, you can use IQKeyboardManager whose pod is available in cocoapods
and it will manage of when clicking on cell's textfield , tableview will automatically scroll.
Try to use third party library IQKeyboardManager it will help you to solve this issue.
https://github.com/hackiftekhar/IQKeyboardManager
Replace following code in your AppDelegate.m file and then you don't need to use scrollview method to handle keyboard :-
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[IQKeyboardManager sharedManager] setEnable:YES];
[self.window makeKeyAndVisible];
return YES;
}
Have you created separate UITableViewCell class ?
I believe as you scroll your UITableView your UI freezes or when you tab twice frequently your UITableView stops responding or taking time to respond.
Here is a working example. You use your scroll view's contentInset and scrollIndicatorInsets properties to avoid the keyboard when it appears. You should register for keyboard notifications. Use the info in the notifications to determine the size of the keyboard--you never know what that will be.
Using content inset is the correct way to handle this for scroll views. If you subsequently need to scroll your editing row into view, use UITableView.scrollToRowAtIndexPath(_:, atScrollPosition:, animated:)
Notice that the does the right thing when the user hides/shows the completions bar.
import UIKit
import CoreGraphics
// our Cell class
class Cell : UITableViewCell
{
// cell reuse identifier for table view
static let Identifier = "Cell"
// the object the cell represents/displays. Could be anything you like
var value:AnyObject? {
didSet {
// update our text field when our cell value is set.
self.textField.text = value as? String
}
}
// use a text field to display our contents, since that allows editing and showing the keyboard
lazy var textField:UITextField = {
let textField = UITextField()
self.contentView.addSubview( textField )
return textField
}()
override func layoutSubviews() {
super.layoutSubviews()
self.textField.frame = contentView.bounds.insetBy(dx: 20, dy: 4 )
}
}
// table view data source class
class DataSource : NSObject, UITableViewDataSource
{
var numberOfRows:Int { return items.count }
let items = [ "Seoul", "São Paulo", "Bombay", "Jakarta", "Karachi", "Moskva", "Istanbul", "Mexico", "Shanghai", "Tokyo", "New" York, "Bangkok", "Beijing", "Delhi", "London", "Hong Kong", "Cairo", "Tehran", "Bogota", "Bandung", "Tianjin", "Lima", "Rio de Janeiro", "Lahore", "Bogor", "Santiago", "St Petersburg", "Shenyang", "Calcutta", "Wuhan" ]
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
{
return numberOfRows
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
{
let cell = tableView.dequeueReusableCellWithIdentifier( Cell.Identifier ) as? Cell ?? Cell()
cell.value = items[ indexPath.row ]
return cell
}
}
class ViewController : UIViewController
{
override func viewDidLoad() {
super.viewDidLoad()
// register for notifications when the keyboard appears:
NSNotificationCenter.defaultCenter().addObserver( self, selector: "keyboardWillShow:", name: UIKeyboardWillChangeFrameNotification, object: nil)
}
override func viewDidLayoutSubviews() {
tableView.frame = view.bounds
}
lazy var tableView:UITableView = {
let tableView = UITableView()
self.view.addSubview( tableView )
tableView.dataSource = self.dataSource
tableView.delegate = self
return tableView
}()
lazy var dataSource : DataSource = DataSource()
// Handle keyboard frame changes here.
// Use the CGRect stored in the notification to determine what part of the screen the keyboard will cover.
// Adjust our table view's contentInset and scrollIndicatorInsets properties so that the table view content avoids the part of the screen covered by the keyboard
#objc func keyboardWillShow( note:NSNotification )
{
// read the CGRect from the notification (if any)
if let newFrame = (note.userInfo?[ UIKeyboardFrameEndUserInfoKey ] as? NSValue)?.CGRectValue() {
let insets = UIEdgeInsetsMake( 0, 0, newFrame.height, 0 )
tableView.contentInset = insets
tableView.scrollIndicatorInsets = insets
}
}
}
// need to conform to UITableViewDelegate protocol since we are the table view's delegate
extension ViewController : UITableViewDelegate
{
}
// App set up stuff here:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
lazy var window:UIWindow? = UIWindow()
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window!.rootViewController = ViewController()
window!.makeKeyAndVisible()
return true
}
}