I want to make a tab bar with 5 tab items. I want the middle one ( third ) to be out of tab bar's corner it may be tough to understand therefore I decided to add screenshot
I want to make something like you can see above but I don't know how it's possible.
I would appreciate any way you recommend to do it.
select tabbar Item and set its image insets
Make sure to get proper image (if using an image) goto Assets-> select desired image -> set property to always original
create a class of UITabBarController and assign it to the TabBarController.
initialise Variable
let button = UIButton.init(type: .custom)
Then in viewDidLoad
override func viewDidLoad() {
super.viewDidLoad()
button.setImage(UIImage(named: "IMAGE_NAME_FROM_ASSETS"), for: .normal)
button.backgroundColor = UIStyle.Color.CameraBG
button.layer.cornerRadius = 40
button.addShadow(offset: CGSize(width: 5, height: 5), color: UIStyle.Color.CameraShadow, radius: 5, opacity: 0.1)
button.addTarget(self, action: #selector(pressedAction(_:)), for: .touchUpInside)
self.view.insertSubview(button, aboveSubview: self.tabBar)
}
Add viewDidLayoutSubviews in your class
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// safe place to set the frame of button manually
button.frame = CGRect.init(x: self.tabBar.center.x - 40, y: self.view.bounds.height - 100, width: 80, height: 80)
}
Action You Want To Perform on button click
#objc func pressedAction(_ sender: UIButton) {
// do your stuff here
let nc = UINavigationController(rootViewController: YOURVC.storyboardInstance())
nc.modalPresentationStyle = .fullScreen
present(nc, animated: true, completion: nil)
}
Related
I have a problem working on the UI. I was using a navigationController to move the screen.
But I didn't want to use the basic back button, so I hid the basic back button, added the navigation bar to the storyboard,
and added items to make the back button. However, the screen does not move to the previous screen. How can I get to the previous screen?
Basic backButton // I'm hiding it now.
I want to use the back button I made.
Backward command for back button items.
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
self.navigationController?.setNavigationBarHidden(true, animated: animated)
}
#IBAction func backButton(_ sender: UIBarButtonItem) {
self.dismiss(animated: false, completion: nil) // Did not worked!
}
In addition, I would like to remove the underline of the navigation header. How can I remove it?
I use this in a project for the same situation:
UIApplication.shared.keyWindow?.rootViewController?.dismiss(animated: false, completion: nil)
this can be used after iOS 13 deprecation of keyWindow:
let keyW = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
keyW?.rootViewController?.dismiss(animated: false, completion: nil)
This can also be used:
self.navigationController?.popViewController(animated: false)
or
self.navigationController?.popToRootViewController(animated: false)
About the button:
Why not just use the navigation button by creating this hierarchy in storyboard:
Then just change the image for btn to your custom image (I use burger1.png) like this:
You can customize back button with following code
let buttonView = UIView(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
let buttonImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 25, height: 25))
buttonImageView.contentMode = .scaleAspectFit
buttonImageView.image = UIImage(named: "back_button")
let btnLogo = UIButton(frame: CGRect(x: 0, y: 0, width: 50, height: 50))
btnLogo.setTitle("", for: .normal)
btnLogo.backgroundColor = UIColor.clear
btnLogo.layer.masksToBounds = true
btnLogo.addTarget(self, action: #selector(backButton), for: .touchUpInside)
buttonView.addSubview(buttonImageView)
buttonView.addSubview(btnLogo)
let barButton = UIBarButtonItem(customView: buttonView)
self.navigationItem.leftBarButtonItem = barButton
if you are using navigationbar you should use navigationController.popViewController instead of dismiss method
I would like to place a "forgot?" Button into my Password Textfield. If nothing is in the Textfield the user should be able to click it and another ViewController should pop up. The only thing I managed to do is what you can see in the picture down below. My problem is that the button is not clickable and that it is not on the same level as the placeholder text. Any ideas on how to solve this two problems?
let button = UIButton(type: .custom)
button.setTitle("vergessen?", for: .normal)
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
passwordTextField.rightView = button
passwordTextField.rightViewMode = .unlessEditing
In the file you have subclassed from my answer add another function in that file
// Modify the values as required
override func rightViewRect(forBounds bounds: CGRect) -> CGRect {
let offset = -20
let width = 100
let height = width
let x = Int(bounds.width) - width - offset
let y = offset
let rightViewBounds = CGRect(x: x, y: y, width: width, height: height)
return rightViewBounds
}
Now you can remove the follow line
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
Output
And regarding the button click event. Remove your code as you mention its not connected
IBAction func refresh(_ sender: Any) { }
And add the following code in the same file where the button is created.
#objc func refresh() {
// your vc code here
print("in refresh")
}
The above code hooks in with addTarget code you have.
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
Hope this helps.
I suggest to create a xib file and its relevant view for text items that have a button inside it. So you would be able to reuse it elsewhere in the future (this or another projects)
By defining constant height (100) you will experience ugly and misplaced UI in different iOS devices.
Here it is what you should do :
Define a xib file for your custom UITextView
Create constraints for it so it width and height defined by its parent.Also define your forgot UIButton in relative to your UITextView.
Define its (xib) relevant UIView class
Use it in your Storyboard
You can use Storyboard. Make a helper view for the password TextField and Forgot Button.
-Set the Helper view same width and height with the email TextField.
-Add a TextField and a Button inside the helper view and then you can decide for the password TextField width and the Forgot Button width.
-Set constrains for the TextField and Button
I set green color to understand what the helper View does.
Update
I just used your code and it works fine.Check your textfield constrains again. This is what I used.
override func viewDidLoad() {
super.viewDidLoad()
let button = UIButton(type: .system)
button.setTitle("vergessen?", for: .normal)
button.setTitleColor(#colorLiteral(red: 0.3647058904, green: 0.06666667014, blue: 0.9686274529, alpha: 1), for: .normal)
button.frame = CGRect(x: CGFloat(0), y: CGFloat(0), width: CGFloat(100), height: CGFloat(100))
button.addTarget(self, action: #selector(self.refresh), for: .touchUpInside)
textfFeld.rightView = button
textfFeld.rightViewMode = .unlessEditing
}
#objc func refresh(_ sender: Any) {
print("Hello")
}
The only think I changed is the button type from .custom to .system.
actually I don't know the correct way how to make an image inside the navigation bar like this, either using navigation controller or by using custom view by myself
I need to insert these 2 image as bar button and image title
so I tried to use navigation controller and insert an image in viewDidLoad like the code below:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// insert image title
let aspectRatio : CGFloat = 0.25
let widthOfImageHeader = view.frame.width * 0.5
let heightOfImageHeader = widthOfImageHeader * aspectRatio
let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: widthOfImageHeader, height: heightOfImageHeader))
imageView.contentMode = .scaleAspectFit
let image = UIImage(named: "testImage2")
imageView.image = image
navigationItem.titleView = imageView
// set bar button image
//create a new button
let button = UIButton(type: .custom)
//set image for button
button.setImage(UIImage(named: "hamburgerIcon"), for: .normal)
//set frame
button.frame = CGRect(x: 0, y: 0, width: 53, height: 51)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
self.navigationItem.leftBarButtonItem = barButton
}
}
but the result is like the image below:
the position of image title is not exactly in the center for iPhone 5s and bar button seems a little bit off in the right.
and if I assign the image right bar button, using
/
/create a left button
let button = UIButton(type: .custom)
//set image for button
button.setImage(UIImage(named: "hamburgerIcon"), for: .normal)
//set frame
button.frame = CGRect(x: 0, y: 0, width: 53, height: 51)
let barButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
self.navigationItem.leftBarButtonItem = barButton
//create a right button
let button2 = UIButton(type: .custom)
//set image for button
button2.setImage(UIImage(named: "backButton"), for: .normal)
//set frame
button2.frame = CGRect(x: 0, y: 0, width: 53, height: 51)
let barRightButton = UIBarButtonItem(customView: button)
//assign button to navigationbar
self.navigationItem.rightBarButtonItem = barRightButton
the result is even weirder
one of the button dissapears, the hamburger icon that should be on the left, now in the right
it will be far easier if I use custom view, but...is it weird if I use custom view that use like navigation bar? I am new in iOS Developer. Thanks in advance
Okay so here's what I usually do in a project with such kind of requirements (image at the navBar's title, and custom bar button items).
To answer first your question, you can actually do whatever you want.
Have indeed a custom view while having your viewController embedded inside a UINavigationController. But be sure to hide the navBar.
Have a visible navBar and viewContorller embedded in UINavigationController.
The ideal way for me is the latter.
The sample project below was made using my own old framework: https://github.com/glennposadas/gpkit-ios
You can copy any pieces of codes from that framework, modify/rename everything on it, and put in your production project.
If you want the result below, here's how I do it:
import GPKit
import UIKit
class ViewController: UIViewController {
// MARK: - Properties
internal lazy var button_Close: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "ham"), for: .normal)
button.imageEdgeInsets = UIEdgeInsets.init(top: 0, left: -30, bottom: 0, right: 0)
//button.addTarget(self, action: #selector(hamburger(_:)), for: .touchUpInside)
return button
}()
// MARK: - Functions
// MARK: Overrides
override func viewDidLoad() {
super.viewDidLoad()
GPLog(classSender: self, log: "viewDidLoad!")
// Title View Test: -
let navBarImage = UIImage(named: "ic_logo_navbar")!
self.setNavBarTitleWithFeedback(image: navBarImage, navBarTintColor: .white)
self.makeNavBarColor(color: UIColor.colorWithRGBHex(0x332F39), itemsTintColor: .white)
// Barbutton
let barButton = UIBarButtonItem(customView: self.button_Close)
self.button_Close.frame = CGRect(x: 0, y: 0, width: 55.0, height: 44.0)
let negativeSpacer = UIBarButtonItem(barButtonSystemItem: UIBarButtonItem.SystemItem.fixedSpace, target: nil, action: nil)
if #available(iOS 11.0, *) {
negativeSpacer.width = -30
}
self.navigationItem.leftBarButtonItems = [negativeSpacer, barButton]
}
}
extension ViewController: GPTitleViewDelegate {
func gpTitleView(userDidTapTitleView gpTitleView: GPTitleView) {
GPLog(classSender: self, log: "userDidTapTitleView🌈")
}
func gpTitleView(userDidFinishLongPress gpTitleView: GPTitleView) {
GPLog(classSender: self, log: "userDidFinishLongPress🌺")
}
}
Result:
I don't have your hex color, so it looks ugly.
I hope thish elps.
I created a navigation bar which I'm trying to call in another view controller. I set it up by calling the methods which I separated into left, center and right buttons. In my other controller I call the navbarcontroller and try and call the method for which i setup the navigation toolbar. Nothing happens, however there is no crash.
import UIKit
class NavBarController : UIViewController{
var screenSize: CGRect!
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBarItems()
setupToolBarItems()
self.navigationController?.isToolbarHidden = false
self.view!.backgroundColor = .white
}
and my method for the navigation bar is this
func setupNavigationBarItems() {
setupCenterNavButton()
setupLeftNavButton()
setupRightNavButton()
}
func showCalendarController() {
let navController = CalendarController()
self.present(navController, animated: true, completion: nil)
} //connect bottom bar buttons to controller
func showEventsController() {
let navController = EventsController()
self.present(navController, animated: true, completion: nil)
} //connect bottom bar buttons to controller
func setupNavigationBarItems() {
setupCenterNavButton()
setupLeftNavButton()
setupRightNavButton()
} // top bar button setup
private func setupCenterNavButton() {
let buttonFrame = UIView(frame: CGRect(x: 0, y: 0, width: 165,
height: 20))
mainFeedButton.frame = CGRect(x: 0,y: 0, width: 80,height: 20) as
CGRect
mainFeedButton.backgroundColor = UIColor.blue
peekFeedButton.frame = CGRect(x: 85,y: 0, width: 80,height: 20) as
CGRect
peekFeedButton.backgroundColor = UIColor.blue
buttonFrame.addSubview(mainFeedButton)
buttonFrame.addSubview(peekFeedButton)
navigationItem.titleView = buttonFrame
} //center bar buttons / action setup
private func setupLeftNavButton() {
navigationItem.leftBarButtonItem = UIBarButtonItem(customView:
favoriteButton)
}// left bar buttons / action setup
private func setupRightNavButton() {
navigationItem.rightBarButtonItem = UIBarButtonItem(customView:
moreButton)
} //right bar buttons / action setup
lazy var mainFeedButton: UIButton! = {
let button = UIButton(type: .custom) // button type
button.setTitle("Main",for: .normal) //button title
button.sizeToFit() // size button to fit the title
var frame = button.frame //create frame to manipulate the body
button.frame = CGRect(x: 0, y: 0, width: 100, height: 40)
button.addTarget(self, action:
#selector(self.showMainFeedController),
for: .touchUpInside)
return button
}() //mainFeed button connected to Feed Controller
lazy var peekFeedButton: UIButton! = {
let button = UIButton(type: .custom) //button type
button.setTitle("Spy",for: .normal) //button title
button.sizeToFit() // size button to fit the title
var frame = button.frame //create frame to manipulate the body
button.frame = CGRect(x: 20, y: 0, width: 100, height: 40)
button.addTarget(self, action:
#selector(self.showSpyFeedController),
for: .touchUpInside)
return button
}()//peekFeed button frame and action setup
lazy var favoriteButton: UIButton! = {
let button = UIButton(type: .system) //default button with blue
text
button.setImage(#imageLiteral(resourceName:
"star").withRenderingMode(.alwaysOriginal), for: .normal)
button.contentMode = .scaleAspectFit
button.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
button.addTarget(self, action: #selector(favoriteButton_tapped),
for: .touchUpInside)
return button
}() //favorites button frame and action setup
lazy var moreButton: UIButton! = {
let button = UIButton(type: .system) //default button with blue
text
button.setImage(#imageLiteral(resourceName:
"more").withRenderingMode(.alwaysOriginal), for: .normal)
button.contentMode = .scaleAspectFit
button.frame = CGRect(x: 0, y: 0, width: 24, height: 24)
button.addTarget(self, action: #selector(moreButton_tapped),
for: .touchUpInside)
return button
}() //more button frame and action setup
func showMainFeedController() {
let navController = MainFeedController()
self.present(navController, animated: true, completion: nil)
} //mainFeed button connected to Feed Controller
func showSpyFeedController() {
let navController = SpyFeedController()
self.present(navController, animated: true, completion: nil)
}//peekFeed button connected to SpyFeedController
func favoriteButton_tapped(sender: UIButton) {
print("You touched this!")
}
func moreButton_tapped(sender: UIButton) {
print("You touched this!")
}
}
I then try and call the function by setupNavigationBarItems() like this
import UIKit
class EventsController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
let navbar = NavBarController()
navbar.setupNavigationBarItems()
self.navigationController?.isToolbarHidden = false
self.view.backgroundColor = .white
}
}
I'm not sure if this a valid way. I'm still kinda new to all of this.
It's not clear what you expect to happen, but here's what does happen:
let navbar = NavBarController()
A completely new NavBarController object is created.
navbar.setupNavigationBarItems()
That NavBarController object's setupNavigationBarItems is called.
self.navigationController?.isToolbarHidden = false
self.view.backgroundColor = .white
Your code comes to an end. navbar was a local variable, so the NavBarController object vanishes in a puff of smoke. The end. This object was created and configured to no purpose.
I remember my first month in iOS way back 2015 :D, didn't have any knowledge in OOP, I didn't know too how to pass a data to another screen or class.
Anyways, you DO NOT create a new instance of your NavBarController class in your EventsController. If you want to talk to your NavBarController from your EventsController, then you will need a reference that is currently alive. You can also use delegate (search for that later).
So before you show or present your EventsController from your NavBarController, pass your current NavBarController instance to the next screen which is EventsController. BUT FIRST, you need to declare a variable in your EventsController, correct? :)
Declare a variable with a type of NavBarController inside your EventsController class, like so:
var navBarController: NavBarController!
Then in this piece of code of yours, pass your self (the NavBarController instance) to the EventsController class before showing or presenting, take note that you mistakenly gave a wrong name to your EventsController new instance, so I renamed it:
func showEventsController() {
let eventsController = EventsController()
eventsController.navBarController = self // THIS :)
self.present(eventsController, animated: true, completion: nil)
}
Lastly, instead of this:
let navbar = NavBarController()
navbar.setupNavigationBarItems()
Make use of your declared variable, like so:
self.navBarController.navbar.setupNavigationBarItems()
Hope this helps! :)
I have multiple navigation controllers and their root view controllers in my app. I want each navigation bar to have social media buttons closely placed on the right side of the bar. For the same I have used this code to show the buttons in 1 view controller:
let fbImage = UIImage(named: "Facebook.png")!
let twitterImage = UIImage(named: "Twitter.png")!
let youtbImage = UIImage(named:"YouTube.png")!
let fbBtn: UIButton = UIButton(type: .custom)
fbBtn.setImage(fbImage, for: UIControlState.normal)
fbBtn.addTarget(self, action: #selector(HomeViewController.fbBtnPressed), for: UIControlEvents.touchUpInside)
fbBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let fbBarBtn = UIBarButtonItem(customView: fbBtn)
let twitterBtn: UIButton = UIButton(type: .custom)
twitterBtn.setImage(twitterImage, for: UIControlState.normal)
twitterBtn.addTarget(self, action: #selector(HomeViewController.twitterBtnPressed), for: UIControlEvents.touchUpInside)
twitterBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let twitterBarBtn = UIBarButtonItem(customView: twitterBtn)
let youtbBtn: UIButton = UIButton(type: .custom)
youtbBtn.setImage(youtbImage, for: UIControlState.normal)
youtbBtn.addTarget(self, action: #selector(HomeViewController.youtubeBtnPressed), for: UIControlEvents.touchUpInside)
youtbBtn.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
let youtbBarBtn = UIBarButtonItem(customView: youtbBtn)
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
Now I want the same buttons on all navigations bars. I can easily copy this code and respective target methods in viewDidLoad() of each view controller, but too much code is getting repeated. So how can avoid this situation?
I am using Swift 3. I am new to iOS. Any help will be appreciated!
Most duplications are solved by using functions. The first step is to extract that code into a function, the second step is to use the same function from multiple places.
You can add it to an extension, for example:
extension UIViewController {
func addShareButtons() {
...
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
}
}
and only call
self.addShareButtons()
from every controller that needs the buttons.
You can add button handlers to the extension too.
Another method is to use a UIViewController subclass but that's always a problem if you want to use a UITableViewController subclass.
You can design a custom navigation controller and add these code to the navigation controller. inherit the navigation controller to your storyboard or programmatically where you want to use.
//sample code
class "YourNavigationCorollerName": UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
//declare here you code, what you want
}
}
Assuming that these buttons ALWAYS have the same behavior, you can create a custom class for them, and place the repeated code there. For instance:
class FacebookButton : UIButton {
override init() {
super.init()
self.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
self.setImage(UIImage(named: "Facebook.png")!, for: .normal)
}
}
...I think the compiler will yell at you for initializing a UIView without a decoder and frame, but hopefully you get the idea.
To improve on this, you could 1) create a custom UINavigationController with the UIBarButtonItems you want to use, and reuse that controller, and/or 2) create a protocol like the following:
protocol FacebookBtnHandler {
func fbBtnPressed()
}
...and have any relevant VCs conform to it. This would allow you to assign the target and selector for the button in the FacebookButton init method, where you assign the image and frame, and hence prevent repetition of that line, as well.
Try to implement bar button by creating subclass of UIBarButton
class Button: UIBarButtonItem {
convenience init(withImage image : UIImage,Target target: Any, andSelector selector: Any?){
let button = UIButton(type: .custom)
button.setImage(image, for: UIControlState.normal)
button.frame = CGRect(x: 0, y: 0, width: 30, height: 30)
button.addTarget(target, action: selector, for: UIControlEvents.touchUpInside)
self.init(customView: button)
}
}
let fbImage = UIImage(named: "Facebook.png")!
let twitterImage = UIImage(named: "Twitter.png")!
let youtbImage = UIImage(named:"YouTube.png")!
let fbBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.fbBtnPressed))
let twitterBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.twitterBtnPressed))
let youtubeBtn = Button(withImage: fbImage, Target: self, andSelector: #selector(HomeViewController.youtubeBtnPressed))
self.navigationItem.setRightBarButtonItems([youtbBarBtn, twitterBarBtn, fbBarBtn], animated: false)
and make it for all your view controller
extension UIViewController {
func addButtons() {
// add above code
}
}