I am working with one project on Xcode with SnapKit and without storyboard. I didn't touch AppDelegate. I have this code on SceneDelegate.
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
guard let windowScene = (scene as? UIWindowScene) else { return }
window = UIWindow(windowScene: windowScene)
window?.makeKeyAndVisible()
window?.rootViewController = TabBarController()
}
}
Project's hierarchy like that:
ShopPage(Group) ...
MessagePage(Group) ...
ProfilePage(Group)
I want to open SettingsViewController when settingsButton(on ProfileViewController) tapped.
This is my code in the TabBarController:
import UIKit
class TabBarController: UITabBarController {
override func viewDidLoad() {
super.viewDidLoad()
self.setupTabs()
selectedIndex = 3
tabBar.backgroundColor = .systemBackground
tabBar.tintColor = UIColor(red: 0, green: 155/255, blue: 247/255, alpha: 1)
}
//MARK: - Tab Setup
private func setupTabs(){
let home = self.createNav(title: "Home", image: UIImage(systemName: "house"), vc: ViewController())
let shop = self.createNav(title: "Shop", image: UIImage(systemName: "bag"), vc: ShopViewController())
let message = self.createNav(title: "Message", image: UIImage(systemName: "ellipsis.message"), vc: MessageViewController())
let profile = self.createNav(title: "Profile", image: UIImage(systemName: "person"), vc: ProfileViewController())
self.setViewControllers([home, shop, message, profile], animated: true)
}
private func createNav(title: String, image: UIImage?, vc: UIViewController) -> UINavigationController{
let nav = UINavigationController(rootViewController: vc)
nav.tabBarItem.title = title
nav.tabBarItem.image = image
return nav
}
}
This is my code in the ProfileViewController:
class ProfileViewController: UIViewController {
private lazy var settingButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "gearshape"), for: .normal)
button.addTarget(self, action: #selector(openSettings), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor(red: 250/255, green: 250/255, blue: 250/255, alpha: 1)
setupUI()
}
@objc private func openSettings() {
let settingsViewController = SettingsViewController()
navigationController?.pushViewController(settingsViewController, animated: true)
}
}
private extension ProfileViewController {
func setupUI() {
setupViews()
setupConstraints()
}
func setupViews() {
view.addSubview(settingButton)
}
func setupConstraints() {
settingButton.snp.makeConstraints { make in
make.center.equalToSuperview()
}
}
}
But when I tap settingButton nothing happens.
I tried to set rootViewController like that on Scene Delegate:
window?.rootViewController = UINavigationController(rootViewController: TabBarController())
Also I tried here set ProfileViewController as a rootViewController
Also I tried with present method:
@objc private func openSettings() {
let settingsViewController = SettingsViewController()
present(settingsViewController, animated: true)
}
Edit
Sorry, I thought that this part of code is not related to my problem and didn't provide you with this. My code was like that:
class ProfileViewController: UIViewController {
private lazy var topView: UIView = {
let view = UIView()
return view
}()
private lazy var topTitle: UILabel = {
let label = UILabel()
label.text = "Profile"
label.font = UIFont.boldSystemFont(ofSize: 19)
return label
}()
private lazy var settingButton: UIButton = {
let button = UIButton()
button.setImage(UIImage(systemName: "gearshape"), for: .normal)
button.addTarget(self, action: #selector(openSettings), for: .touchUpInside)
return button
}()
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
}
@objc private func openSettings() {
print("button")
let settingsViewController = SettingsViewController()
navigationController?.pushViewController(settingsViewController, animated: true)
}
}
// MARK: - setting iui methods
private extension ProfileViewController {
func setupUI() {
setupViews()
setupConstraints()
view.backgroundColor = .systemBackground
}
func setupViews() {
view.addSubview(topView)
topView.addSubview(topTitle)
topView.addSubview(settingButton)
}
func setupConstraints() {
topView.snp.makeConstraints { make in
make.top.equalToSuperview().offset(50)
make.leading.trailing.equalToSuperview()
make.height.equalToSuperview().multipliedBy(0.08)
}
topTitle.snp.makeConstraints { make in
make.center.equalToSuperview()
}
settingButton.snp.makeConstraints { make in
make.trailing.equalToSuperview().offset(-10)
make.height.equalToSuperview().multipliedBy(0.4)
make.width.equalToSuperview().multipliedBy(0.15)
make.centerY.equalToSuperview()
}
}
}
But it turns out the problem was in the topview, but I don’t know exactly why. If my button is in the topview it is not pressed, but if outside then everything works fine.
Each of your tabs holds a UINavigationController
...
So, your "Profile" tab has a UINavigationController
with a root view controller of ProfileViewController
...
You then add your topView
as a subview of ProfileViewController
's view, and you constrain it to the top of that view -- ignoring the safe-area, which includes the navigation bar.
If I modify your code to add some background colors:
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
topView.backgroundColor = .yellow
navigationController?.navigationBar.backgroundColor = .green.withAlphaComponent(0.5)
}
It looks like this:
You cannot tap a button (or any other UI element) that is covered by the navigation bar.
If you want a "Title" and a right-side button on that navigation bar, manage that with the navigation bar...
class ProfileViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .systemBackground
// don't call anything in your current setupUI() func
//setupUI()
self.title = "Profile"
let b = UIBarButtonItem(image: UIImage(systemName: "gearshape"), style: .plain, target: self, action: #selector(openSettings))
navigationItem.rightBarButtonItem = b
}
@objc private func openSettings() {
print("button")
let settingsViewController = SettingsViewController()
navigationController?.pushViewController(settingsViewController, animated: true)
}
}
Now, it looks like this:
and tapping the Gear button will call openSettings()