I use tabBarController to create a music program and I have questions like how to do it as shown in gif
Questions:
How to do so that when you click on tabBarItem, "presentViewController" worked
How to make it so that the photo does not change color and make it round, only in the third tabBarItem
Preferably without libraries
it should be
My TabBarController
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
// меняет цвет фона tabBar
self.tabBar.barTintColor = .white
// меняет цвет UITabBarItem and Title
UITabBar.appearance().tintColor = UIColor(hex: 0x0077fe, alpha: 1)
//меняет цвет background UITabBar
UITabBar.appearance().barTintColor = UIColor.white
// делает фон серым
for item in self.tabBar.items! {
if let image = item.image {
item.image = image.withRenderingMode(.alwaysOriginal)
}
}
//показывает и переходит в контроллеры
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller3,controller4,controller5], animated: true)
// создает навигационный контроллер для контроллеров
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
print(item.tag)
if item.tag == 0{
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
Player
override func viewDidLoad() {
super.viewDidLoad()
let im = Extension.resizeImage(image:GlobalModals[thisSong].ImageView! , targetSize: CGSize.init(width:20, height: 20))
self.tabBarController?.tabBar.items![2].image = im
}
}
The TabBarController doesn't have those option, you need to implement it by subclassing.
You can use this library Animated Tab Bar to achieve the same result with animation.
I created a view and a button on it in tabBarController and created a func that sets the parameters and call it after I set all the view controllers also created a notificationCenter that could change the button image from another controller
class TabBarController: UITabBarController,UITabBarControllerDelegate {
open var playerBtn = UIButton()
//func for NotificationCenter
#objc func imageChange() {
if GlobalModals.count != 0 {
playerBtn.setImage(GlobalModals[thisSong].ImageView, for: .normal)
}
}
override func viewDidLoad() {
super.viewDidLoad()
NotificationCenter.default.addObserver(self, selector: #selector(imageChange), name: NSNotification.Name(rawValue: "imageChange"), object: nil)
self.delegate = self
//shows and goes to controllers
let storyBoard = UIStoryboard(name: "Main", bundle:nil)
let controller1 = storyBoard.instantiateViewController(withIdentifier: "main") as! VCMain
let controller2 = storyBoard.instantiateViewController(withIdentifier: "search")
let controller3 = storyBoard.instantiateViewController(withIdentifier: "player")
let controller4 = storyBoard.instantiateViewController(withIdentifier: "bookmark")
let controller5 = storyBoard.instantiateViewController(withIdentifier: "menu")
self.setViewControllers([controller1,controller2,controller4,controller5], animated: true)
// creates navigation controller
let vc1 = UINavigationController(rootViewController: controller1)
let vc2 = UINavigationController(rootViewController: controller2)
let vc3 = UINavigationController(rootViewController: controller3)
let vc4 = UINavigationController(rootViewController: controller4)
let vc5 = UINavigationController(rootViewController: controller5)
viewControllers = [vc1, vc2, vc3, vc4, vc5]
self.setupMiddleButton()
}
// TabBarButton – Setup Middle Button and View
func setupMiddleButton() {
playerBtn.frame = CGRect(x: 0, y: 0, width: 45, height: 45)
var playerBtnFrame = playerBtn.frame
playerBtnFrame.origin.y = self.view.bounds.height - playerBtnFrame.height - 2
playerBtnFrame.origin.x = self.view.bounds.width / 2 - playerBtnFrame.size.width / 2
playerBtn.frame = playerBtnFrame
playerBtn.layer.cornerRadius = playerBtnFrame.height/2
playerBtn.layer.masksToBounds = true
playerBtn.contentMode = .scaleAspectFill
let view = UIView()
let width = self.view.frame.width/5
let xWidth = width*2
view.frame = CGRect(x: xWidth , y: playerBtnFrame.origin.y - 2, width: self.view.frame.width/5, height: tabBar.frame.height)
view.backgroundColor = .clear
self.view.addSubview(view)
self.view.addSubview(playerBtn)
playerBtn.setImage(UIImage(named: "home"), for: UIControlState.normal)
playerBtn.addTarget(self, action: #selector(playerButtonAction), for: UIControlEvents.touchUpInside)
self.view.layoutIfNeeded()
}
// Button Touch Action
#objc func playerButtonAction(sender: UIButton) {
if GlobalModals.count != 0 {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "player") as? VCPlayer
self.present(vc!, animated: true, completion: nil)
}
}
}
so I change the image of the button
NotificationCenter.default.post(name: NSNotification.Name(rawValue: "imageChange"), object: nil)
Related
I am trying to show a custom view on a dismiss of a controller. It's working fine if I am sowing it on popViewController but not working when I am using dismiss method. If anybody knows the solution please help me out.
// presenting screen from HomeController
guard let controller = UIStoryboard(name: "EmptyWorkout", bundle: nil).instantiateViewController(withIdentifier: "LogWoroutVC") as? LogWorkoutViewController else { return }
controller.modalPresentationStyle = .overFullScreen
self.present(controller, animated: true, completion: nil)
// dimissing screen from LogWorkoutViewController
#IBAction func backButtonAction(_ sender: UIButton) {
let window = UIApplication.shared.windows.last
let timerView = UINib(nibName: "WorkoutTimer", bundle: nil).instantiate(withOwner: nil, options: nil).first as? WorkoutTimer
if let timerView = timerView {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
if let tabBarYPosition = self.tabBarController?.tabBar.frame.origin.y {
let yPosition = tabBarYPosition - 60
timerView.frame = CGRect(x: 0, y: yPosition, width: screenWidth, height: 60)
window?.addSubview(timerView)
}
}
self.dismiss(animated: true, completion: nil)
}
According to the docs, the presenting controller is responsible for the actual dismiss. When the presented controller dismisses itself, it will ask the presenter to do it for it. In here you need to use the closure or protocol. for e.g use like
on your LogWorkoutViewController VC / presented VC declare the variable as like
var dismissedVC : ((Bool) -> Void)?
There is a Boolean property inside UIViewController called isBeingDismissed that you can use for this purpose:
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if isBeingDismissed {
// TODO: called the closures
dismissedVC!(true)
}
}
on your button action call only dismiss VC
#IBAction func backButtonAction(_ sender: UIButton) {
self.dismiss(animated: true, completion: nil)
}
on your HomeController when you present call the closure also.
guard let controller = UIStoryboard(name: "EmptyWorkout", bundle: nil).instantiateViewController(withIdentifier: "LogWoroutVC") as? LogWorkoutViewController else { return }
controller.modalPresentationStyle = .overFullScreen
controller.dismissedVC = { result in
// call your WorkoutTimer view
self.workoutTimer()
}
self.present(controller, animated: true, completion: nil)
func workoutTimer() {
let window = UIApplication.shared.windows.last
let timerView = UINib(nibName: "WorkoutTimer", bundle: nil).instantiate(withOwner: nil, options: nil).first as? WorkoutTimer
if let timerView = timerView {
let screenSize = UIScreen.main.bounds
let screenWidth = screenSize.width
if let tabBarYPosition = self.tabBarController?.tabBar.frame.origin.y {
let yPosition = tabBarYPosition - 60
timerView.frame = CGRect(x: 0, y: yPosition, width: screenWidth, height: 60)
window?.addSubview(timerView)
}
}
}
I have three viewcontrollers that all have buttons switch to a fourth viewcontroller. On this fourth viewcontroller, I have a back button, which I want to take me back to the viewcontroller that I was originally at.
I can't just use the control drag since that only let's you go to a single controller. How can I programmatically have it send the user to the most recent viewcontroller?
import UIKit
class ViewController: UIViewController {
let tabBar = UITabBarController()
var selectedIndex: Int = 0
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tab()
}
#IBAction func goto(_ sender: Any) {
let storyBoard : UIStoryboard = UIStoryboard(name: "Main", bundle:nil)
let nextViewController = storyBoard.instantiateViewController(withIdentifier: "FrekansViewController") as! FrekansViewController
self.present(nextViewController, animated:true, completion:nil)
}
func tab() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let homeVC = UIViewController()
var streamVC = UIViewController()
var liveVC = UIViewController()
var searchVC = UIViewController()
streamVC = storyboard.instantiateViewController(withIdentifier: "StreamViewController")
liveVC = storyboard.instantiateViewController(withIdentifier: "LiveViewController")
searchVC = storyboard.instantiateViewController(withIdentifier: "SearchViewController")
tabBar.viewControllers = [homeVC,streamVC, liveVC, searchVC]
let itemHome = UITabBarItem(title: "Home", image: UIImage.init(systemName: "house.fill") , tag:0)
let itemStream = UITabBarItem(title: "List", image: UIImage.init(systemName: "waveform.path") , tag:1)
let itemLive = UITabBarItem(title: "Radio", image: UIImage.init(systemName: "play.fill") , tag:2)
let itemSearch = UITabBarItem(title: "Search", image: UIImage.init(systemName: "magnifyingglass"), tag: 3)
homeVC.tabBarItem = itemHome
streamVC.tabBarItem = itemStream
liveVC.tabBarItem = itemLive
searchVC.tabBarItem = itemSearch
self.view.addSubview(tabBar.view)
}
}
Try to embed the controllers inside NavigationController then you can use :
navigationController?.popViewController(animated: true)
This would do the desired functionality for you.
I have an app with a tableView. I decided to add more screens with other tableViews, so I added a tabBarController programatically. Now I get a found nil error on these lines:
tableView.delegate = self
tableView.dataSource = self
If I remove them, the tableView doesn't load. Do you know what I might be doing wrong?
I added a tabBarController on main storyboard an linked it to the swift file, but it also doesn't work.
class SecondVC: UIViewController,UITableViewDelegate, UITableViewDataSource {
#IBOutlet weak var tableView: UITableView!
var SoundsClasss = [SoundsClass]()
override func viewDidLoad() {
super.viewDidLoad()
let p1 = SoundsClass(imageURL: "sound 01", audioURL: "01", videoTitle: "1", duration: 100)
let p2 = SoundsClass(imageURL: "sound 01", audioURL: "02", videoTitle: "2", duration: 100)
SoundsClasss.append(p1)
SoundsClasss.append(p2)
tableView.delegate = self
tableView.dataSource = self
}
The code for the TabBarController. Do I have to change anything here to specify that the view is a tableView?
class MainTabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
tabBar.barTintColor = UIColor.white
setupTabBar()
}
func setupTabBar(){
let FirstController = UINavigationController(rootViewController: MainVC())
let SecondController = UINavigationController(rootViewController: SecondVC())
let ThirdController = UINavigationController(rootViewController: ThirdVC())
viewControllers = [FirstController, SecondController,ThirdController]
guard let items = tabBar.items else { return }
for item in items {
item.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
}
}
}
in class named SecondVC you have an outlet reference for your UITableView instance this means you create your table view in Stroyboard, so you can not create the view controller using the initializer you should use
UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourViewcontrollerID")
the setupTabBar function should be as the following
func setupTabBar(){
let vc1 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourFirstViewcontrollerID")
let vc2 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourSecondViewcontrollerID")
let vc3 = UIStoryboard(name: "yourStoryBoardName", bundle: nil).instantiateViewController(withIdentifier: "yourThirdViewcontrollerID")
let FirstController = UINavigationController(rootViewController: vc1)
let SecondController = UINavigationController(rootViewController: vc2)
let ThirdController = UINavigationController(rootViewController: vc3)
viewControllers = [FirstController, SecondController,ThirdController]
guard let items = tabBar.items else { return }
for item in items {
item.imageInsets = UIEdgeInsets(top: 4, left: 0, bottom: -4, right: 0)
}
}
You're using SecondVC() to create your controller. That does not reference the storyboard so it does not set up any outlets.
You need to either build your view controller hierarchy in the storyboard and let it load by default or else get your controllers from the storyboard before adding them to the navigation controllers.
See documentation for:
func instantiateViewController(withIdentifier identifier: String) -> UIViewController
I'm using PageViewController and tab bar. And I have a question which is 'how to move another view by touching button.' My app has 5 ViewControllers and they are on PageViewController. And the PageViewController is on ViewController. That ViewController is MainController. So I separated Main view, with about 4 : 1(PageViewController : tab bar). So tab bar is on MainController, MainController can changes ViewController when tab item is touched.
But what I want to make is a button on one of five ViewControllers which can change ViewController.
The button has not right to change ViewController. So I searched how to solve this problem but It couldn't find good solution.
I think the solution is very short, about 1 ~ 2 sentences.
I attached my code, so I hope anybody who know how to solve this problem please helps me.
I'm new in Swift, so explain detail please
This is a part of MainController
import UIKit
class ViewController: UIViewController, UIPageViewControllerDataSource, UIPageViewControllerDelegate {
var pageViewController: UIPageViewController!
var subVC: [UIViewController] = []
var viewIndex: Int = 0
var tabIndicator1 = UIView()
var tabIndicator2 = UIView()
var tabIndicator3 = UIView()
var tabIndicator4 = UIView()
var tabIndicator5 = UIView()
var views = [UIView]()
override func viewDidLoad() {
super.viewDidLoad()
createVC() // make five ViewControllers
self.pageViewController = self.storyboard?.instantiateViewController(withIdentifier: "PageViewController") as! UIPageViewController
self.pageViewController = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal, options: nil)
self.pageViewController.view.frame = CGRect(x: 0, y: 0, width: self.view.frame.width, height: self.view.frame.height)
self.addChildViewController(self.pageViewController)
self.view.addSubview(self.pageViewController.view)
let startVC = subVC[0]
viewIndex = 0
let viewControllers = NSArray(object: startVC)
self.pageViewController.setViewControllers(viewControllers as? [UIViewController], direction: .forward, animated: true, completion: nil)
self.pageViewController.didMove(toParentViewController: self)
createTabBar() // make Tab Bar
createIndicator() // make Tab Indicator
self.pageViewController.dataSource = self
self.pageViewController.delegate = self
}
...
func createVC() { // I have five ViewController files
let HomeVC = VCInstance(name: "Home")
let AccessVC = VCInstance(name: "AccessLog")
let RegisterVC = VCInstance(name: "Register")
let SettingVC = VCInstance(name: "Setting")
let HelpVC = VCInstance(name: "Help")
subVC = [HomeVC, AccessVC, RegisterVC, SettingVC, HelpVC]
}
func VCInstance(name: String) -> UIViewController {
return UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: name)
}
....
You can use this code it might be help you.
extension UIViewController {
func goNextPage(animated: Bool = true) {
guard let currentViewController = self.viewControllers?.first else { return }
guard let nextViewController = dataSource?.pageViewController(self, viewControllerAfter: currentViewController) else { return }
setViewControllers([nextViewController], direction: .forward, animated: animated, completion: nil)
}
func goPreviousPage(animated: Bool = true) {
guard let currentViewController = self.viewControllers?.first else { return }
guard let previousViewController = dataSource?.pageViewController(self, viewControllerBefore: currentViewController) else { return }
setViewControllers([previousViewController], direction: .reverse, animated: animated, completion: nil)
}
}
Created a SingleViewApplication in that I have placed a button.
Now clicking on button I need to display a tableView as popover.
The TableViewController is created in xib.
The issue is tableViewController.popoverPresentationController always comes as nil see below code
let filterVC = TableViewController(nibName: "TableViewController", bundle: nil)
var filterDistanceViewController = UINavigationController(rootViewController: filterVC)
filterDistanceViewController.preferredContentSize = CGSize(width: 300, height: 200)
let popoverPresentationViewController = filterDistanceViewController.popoverPresentationController
popoverPresentationViewController?.permittedArrowDirections = .any
if let pop = filterDistanceViewController.popoverPresentationController {
pop.delegate = self
}
in above code
filterDistanceViewController.popoverPresentationController is always coming as nil
Any hint in right direction will be highly appreciated.
Until you've set a modalPresentationStyle on your VC, the popoverPresentationController property will be nil. Ensure that you set the modalPresentationStyle before accessing.
You are not presenting anything, so you need to present the popoverPresentationViewController on the current viewcontroller, for example:
#IBAction func importantButtonPressed(_ sender: UIButton) {
let tableViewController = UITableViewController()
tableViewController.modalPresentationStyle = .popover
present(tableViewController, animated: true, completion: nil)
if let pop = tableViewController.popoverPresentationController {
pop.delegate = self
}
}
You may do like below.
#IBAction func popoverBtnPressed(_ sender: Any) {
let vc2 = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "ViewController2")
vc2.modalPresentationStyle = .popover
vc2.popoverPresentationController?.delegate = self
vc2.popoverPresentationController?.barButtonItem = popoverBtn
vc2.popoverPresentationController?.sourceRect = .zero
present(vc2, animated: true, completion: nil)
}