Open swiftUI view from UIKit code for rightBarItem - swift

This is code of UIKit File.I want to open view which is editButtonCustomizeView and written in swiftUI. How can I take action of this button which navigates to swiftUI
override func viewDidLoad() { super.viewDidLoad()
let editButton = UIBarButtonItem(image: editImage, style: .Plain,
target: self, action: editButtonCustomizeView())
}
Right now getting error is "Cannot convert value of type 'editButtonCustomizeView' to expected argument type 'Selector?' "

That is because the action expects a selector to point to what action is to be called when the button is tapped so you can't pass a view.
If you want to show a SwiftUI view when that button is tapped you have to first add a action to show the viewcontroller, then create the action to show the viewcontroller and lastly add you SwiftUI view in a viewcontroller so it can be used in UIKit.
Adding the action to the button:
let editButton = UIBarButtonItem(image: editImage, style: .Plain, target: self, action: #selector(showSwiftUIView))
Then the action to show the view:
#objc func showSwiftUIView(sender: UIControl) {
let vc = editButtonCustomizeVC()
present(vc, animated: true)
}
And lastly turn your view to a ViewController:
final class editButtonCustomizeVC: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let childView = UIHostingController(rootView: editButtonCustomizeView())
addChild(childView)
childView.view.frame = view.bounds
view.addSubview(childView.view)
childView.didMove(toParent: self)
}
}

Related

UIButton is only responsive sometimes when pressing down [duplicate]

I have a UIBarButtonItem in the right side of my navigation that has an image of a gear and presents my settings view controller. I can get it to work properly when I create the button in setupNavigationBar(), but it doesn't work if I create the button as a property. I can't wrap my head around what would be different about these two scenarios. The button is present in both situations, but the functionality isn't.
This version doesn't work
class DecksController: UIViewController {
let settingsBarButton: UIBarButtonItem = {
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
return barButton
}()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
This version does work
class DecksController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
let settingsBarButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
As you've discovered, it makes a big difference where this line occurs:
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"),
style: .plain, target: self, action: #selector(presentSettings))
The problem is the target:self part. When the bar button item is configured as part of an instance property initializer (your first example), the instance doesn't exist yet — it is what we are initializing. So self has no meaning, and the button ends up with no target. Therefore, tapping the button does nothing.
(Actually, to be quite technical, self is the class, but that's not a helpful thing to know.)
In your second example, that line is part of viewDidLoad, which runs considerably after the view controller instance has come into existence and has been initialized. viewDidLoad is an instance method, in fact. So self is the instance, as you expect.

How to set title of navigation item after open another view controller using with present modally - Swift

I have two view controllers in my application. Root view controller has some components and one of them is a button. That button provides to open another view controller using with present function. Second view controller(SelectTimeViewController) that opens when the button is tapped, was opened successfully. I am trying to set navigation title and items but I can not see them.
I did not use storyboard so root view controller is setting from AppDelegate.
let viewController = ViewController()
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: viewController)
window?.makeKeyAndVisible()
When tapped the button, "openVC" function is invoked.
#IBAction func openVC(_ sender: Any) {
self.navigationController?.present(SelectTimeViewController(), animated: true, completion: nil)
}
I am trying to set title and rightBarButtonItem in SelectTimeViewController's viewDidLoad function but I can not see both of them.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: UIImage(named: "close"), style: .plain, target: self, action: #selector(closeIt))
self.navigationItem.title = "Select Time"
}
In additional to this, I can see both title and right bar button item when change the "openVC" function like as bellow.
#IBAction func openVC(_ sender: Any) {
self.navigationController?.pushViewController(vc, animated: true)
}
You have to push the SelectTimeViewController instead of present
Or if you really want to present it, you should present another UINavigationController that has the rootViewController as SelectTimeViewController()

UIBarButtonItem doesn't work when created as a property, but does when created in a function

I have a UIBarButtonItem in the right side of my navigation that has an image of a gear and presents my settings view controller. I can get it to work properly when I create the button in setupNavigationBar(), but it doesn't work if I create the button as a property. I can't wrap my head around what would be different about these two scenarios. The button is present in both situations, but the functionality isn't.
This version doesn't work
class DecksController: UIViewController {
let settingsBarButton: UIBarButtonItem = {
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
return barButton
}()
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
This version does work
class DecksController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
setupNavigationBar()
}
#objc func presentSettings() {
let settingsController = SettingsController()
self.navigationController?.pushViewController(settingsController, animated: true)
}
func setupNavigationBar() {
let settingsBarButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"), style: .plain, target: self, action: #selector(presentSettings))
self.navigationItem.rightBarButtonItem = settingsBarButton
}
}
As you've discovered, it makes a big difference where this line occurs:
let barButton = UIBarButtonItem(image: #imageLiteral(resourceName: "settings"),
style: .plain, target: self, action: #selector(presentSettings))
The problem is the target:self part. When the bar button item is configured as part of an instance property initializer (your first example), the instance doesn't exist yet — it is what we are initializing. So self has no meaning, and the button ends up with no target. Therefore, tapping the button does nothing.
(Actually, to be quite technical, self is the class, but that's not a helpful thing to know.)
In your second example, that line is part of viewDidLoad, which runs considerably after the view controller instance has come into existence and has been initialized. viewDidLoad is an instance method, in fact. So self is the instance, as you expect.

Views not performing segue

I have a collection of views and I want to make that when they are tapped, it will perform the same segue. and no view performs any segue.
class ViewController: UIViewController {
#IBOutlet var categoryViews: [UIView]!
let tapGesture = UIGestureRecognizer(target: self, action: #selector(ViewController.move(tap:)))
override func viewDidLoad() {
super.viewDidLoad()
for category in (0..<categoryViews.count) {
categoryViews[category].addGestureRecognizer(tapGesture)
categoryViews[category].isUserInteractionEnabled = true
}
// Do any additional setup after loading the view.
}
#objc func move(tap: UIGestureRecognizer) {
performSegue(withIdentifier: "Animals", sender: nil)
}
}
A single instance of UITapGestureRecognizer can be added to a single view.
In your code, since you're using a single instance of UITapGestureRecognizer for each view, the tapGesture will be added only to the last view in categoryViews array.
You need to create different UITapGestureRecognizer instance for each view in categoryViews, i.e.
class ViewController: UIViewController {
#IBOutlet var categoryViews: [UIView]!
override func viewDidLoad() {
super.viewDidLoad()
categoryViews.forEach {
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(move(tap:)))
$0.addGestureRecognizer(tapGesture)
$0.isUserInteractionEnabled = true
}
}
#objc func move(tap: UITapGestureRecognizer) {
performSegue(withIdentifier: "Animals", sender: nil)
}
}
The problem is that this code doesn't do what you think it does:
class ViewController: UIViewController {
let tapGesture = UIGestureRecognizer(target: self, action: #selector(ViewController.move(tap:)))
Your let tapGesture is an instance property declaration, and what follows the equal sign is its initializer. But you can't speak of self in an instance property initializer; there is no instance yet. So self here is taken to be the class. Thus, your tap gesture recognizer "works", but the move message is not sent to your ViewController instance; in effect, it is sent into empty space.
To fix this, you can initialize tapGesture at a time when self does exist. For example:
class ViewController: UIViewController {
let tapGesture : UIGestureRecognizer!
func viewDidLoad() {
self.tapGesture = UITapGestureRecognizer(target: self, action: #selector(ViewController.move(tap:)))

Search bar not aligned next to back button in navigation bar

I used a custom navigation controller class to remove the back button in my navigation bar, with the following:
class CustomNavigationController: UINavigationController, UINavigationControllerDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.delegate = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
func navigationController(_ navigationController: UINavigationController, willShow viewController: UIViewController, animated: Bool) {
viewController.navigationItem.backBarButtonItem = UIBarButtonItem(title: " ", style: .plain, target: nil, action: nil)
}
}
In one of my VCs, I also add a search controller to the navigation bar with the following:
private func configureSearchController() {
self.searchController = UISearchController(searchResultsController: nil)
self.searchController.searchResultsUpdater = self
self.searchController.delegate = self
self.searchController.searchBar.delegate = self
self.searchController.hidesNavigationBarDuringPresentation = false
self.searchController.dimsBackgroundDuringPresentation = true
self.navigationItem.titleView = searchController.searchBar
self.definesPresentationContext = true
}
However, it seems like the search bar is still being offset by some blank space where the title would be. I'd image the search bar should be aligned next to the back button, but there is the back button, space with blank title, then search bar.
How can I get the search bar aligned next to the back button instead of spaced because it seems there is some room for the blank title. Also, as a note, tapping on that blank space moves me back to the previous VC, although there is no actual title there.
EDIT:
View hierarchy, looks like the back button container view itself takes up a good amount of space
Try adding this in viewDidLoad
let backButton = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil)
backButton.setBackButtonTitlePositionAdjustment(UIOffsetMake(-1000, -1000), for: .default)
navigationItem.backBarButtonItem = backButton