'UIStoryboard.type' does not have member named 'centerViewController' - swift

I am following THIS tutorial but I didn't downloaded starter project file from that tutorial because I want to make it differently but I am stuck here since an hour because I got this Error:
'UIStoryboard.type' does not have member named 'centerViewController'
Here I am trying to add subView (CenterViewController) into ContainerViewController.
Here is my code for ContainerViewController.swift
import UIKit
import QuartzCore
class ContainerViewController: UIViewController, CenterViewControllerDelegate {
var centerNavigationController: UINavigationController!
var centerViewController: CenterViewController!
override init() {
super.init(nibName: nil, bundle: nil)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
centerViewController = UIStoryboard.centerViewController()
//'UIStoryboard.type' does not have member named 'centerViewController'
centerViewController.delegate = self
// wrap the centerViewController in a navigation controller, so we can push views to it
// and display bar button items in the navigation bar
centerNavigationController = UINavigationController(rootViewController: centerViewController)
view.addSubview(centerNavigationController.view)
addChildViewController(centerNavigationController)
centerNavigationController.didMoveToParentViewController(self)
}
}
This is my CenterViewController.swift
import UIKit
#objc
protocol CenterViewControllerDelegate {
optional func toggleLeftPanel()
optional func collapseSidePanels()
}
class CenterViewController: UIViewController {
var delegate: CenterViewControllerDelegate?
#IBAction func tableTapped(sender: AnyObject) {
}
}
And this is my AppDelegate.swift
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
window = UIWindow(frame: UIScreen.mainScreen().bounds)
let containerViewController = ContainerViewController()
window!.rootViewController = containerViewController
window!.makeKeyAndVisible()
return true
}
Can anyOne give me any Idea what I am missing?

You are missing the extension the tutorial author has provided that makes the UIStoryboard.centerViewController() method exist. The code is at the bottom of ContainerViewController.swift in his downloadable starter project, and I've copied it down below as well:
private extension UIStoryboard {
class func mainStoryboard() -> UIStoryboard { return UIStoryboard(name: "Main", bundle: NSBundle.mainBundle()) }
class func leftViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("LeftViewController") as? SidePanelViewController
}
class func rightViewController() -> SidePanelViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("RightViewController") as? SidePanelViewController
}
class func centerViewController() -> CenterViewController? {
return mainStoryboard().instantiateViewControllerWithIdentifier("CenterViewController") as? CenterViewController
}
}
Add this to the bottom of your ContainerViewController.swift and it should work. (That is, as long as you have these view controllers set up in the right storyboard files with the right identifiers.)

Related

ios XCTest failed Could not find a storyboard named LaunchScreen in bundle NSBundle

I'm trying to run a simple test case where I want to test my view controller but I'm getting this error
Could not find a storyboard named 'LaunchScreen' in bundle NSBundle
I'm running a cocoapods lib through the example folder
this is my test case:
import XCTest
#testable import Welcome
class WelcomeViewControllerTests: XCTestCase {
var viewModel: WelcomeViewModelProtocol!
var viewController: WelcomeViewController!
override func setUpWithError() throws {
viewModel = WelcomeViewModel()
viewController = WelcomeViewController(viewModel: viewModel)
viewController.viewDidLoad()
}
override func tearDownWithError() throws {
viewModel = nil
viewController = nil
}
func testLoadLaunchScreen() throws {
viewController.loadLaunchScreen()
}
}
This is my viewController:
import UIKit
public class WelcomeViewController: UIViewController {
private var viewModel: WelcomeViewModelProtocol
public override func viewDidLoad() {
super.viewDidLoad()
loadLaunchScreen()
let string = viewModel.fetchExample()
debugPrint(string)
}
public init(viewModel: WelcomeViewModelProtocol) {
self.viewModel = viewModel
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func loadLaunchScreen() {
let storyboard = UIStoryboard(name: "LaunchScreen", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "LaunchScreen")
navigationController?.isNavigationBarHidden = true
navigationController?.pushViewController(vc, animated: false)
}
}

RxSwift modelSelected Drive model on model View & Get that model on DetailView

This is my FirstView ( Parent VIew)
tableView.rx.modelSelected(Kinder.self)
.asDriver()
.drive(self.detailKinderViewModel.currentKinder)
.disposed(by: disposeBag)
This is ViewModel ( BehaviorRelay )
lazy var currentKinder = BehaviorRelay<Kinder>(value: Kinder())
This is My SecondView ( Child View )
override func viewDidLoad() {
super.viewDidLoad()
detailKinderViewModel.currentKinder
.asDriver(onErrorJustReturn:Kinder())
.map{$0.kinder_name}
.drive(self.navigationItem.rx.title)
.disposed(by: disposeBag)
}
I can get Only Default Model Data.
I want to Get current Data on Child View
There isn't enough code to figure out what your problem might be. Here is how I would do it using my Cause_Logic_Effect library.
import Cause_Logic_Effect
import RxCocoa
import RxSwift
import UIKit
extension FirstView {
func connect() {
Observable.just([
Kinder(name: "Ben"),
Kinder(name: "Mia"),
Kinder(name: "Leon"),
Kinder(name: "Emma")
])
.bind(to: tableView.rx.items(cellIdentifier: "Cell")) { _, kinder, cell in
if #available(iOS 14.0, *) {
var configuration = cell.defaultContentConfiguration()
configuration.text = kinder.name
cell.contentConfiguration = configuration
}
else {
cell.textLabel?.text = kinder.name
}
}
.disposed(by: disposeBag)
// when the user selects a cell, load its Kinder into a newly created
// SecondView and push it onto the navigation stack
tableView.rx.modelSelected(Kinder.self)
.bind(onNext: pushScene(on: navigationController!, animated: true) { kinder in
SecondView().scene { $0.connect(kinder: kinder) }
})
.disposed(by: disposeBag)
}
}
extension SecondView {
func connect(kinder: Kinder) -> Observable<Never> {
// use the Kinder
title = kinder.name
// this controller doesn't send any info back to its parent.
return .never()
}
}
// The view controllers.
final class FirstView: UIViewController {
var tableView: UITableView!
let disposeBag = DisposeBag()
override func loadView() {
super.loadView()
title = "Main"
tableView = UITableView(frame: view.bounds)
tableView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "Cell")
view.addSubview(tableView)
}
}
final class SecondView: UIViewController {
override func loadView() {
super.loadView()
view.backgroundColor = .white
}
}
// The models
struct Kinder {
let name: String
}
#main
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
// Create a FirstView, configure it with a connector, wrap it in a
// navigation controller and make that the root.
window?.rootViewController = UINavigationController(rootViewController: FirstView().configure { $0.connect() })
window?.makeKeyAndVisible()
return true
}
}

How to customize Firebase Authentication UI in Swift

I am building an authentication system using the Firebase prebuilt UI, and I want to customize the UI to fit the program. Say I want to set the background color to black and change the corner radius of the buttons, is there any way I can do this? I've tried sub-classing the authPickerViewController but somehow it didn't work. I did some searching, but couldn't find any tutorial or recent problems related to this either.
Here's what I have in my MainViewController
class LoginViewController: UIViewController, FUIAuthDelegate{
override func viewDidLoad() {
super.viewDidLoad()
let authUI = FUIAuth.defaultAuthUI()
authUI?.delegate = self
let providers: [FUIAuthProvider] = [
FUIEmailAuth(),
FUIGoogleAuth(),
FUIPhoneAuth(authUI:FUIAuth.defaultAuthUI()!)]
authUI?.providers = providers
let authViewController = authUI?.authViewController()
authViewController!.modalPresentationStyle = .fullScreen
authViewController!.setNavigationBarHidden(true, animated: false)
self.present(authViewController!, animated: false, completion: nil)
}
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
let sourceApplication = options[UIApplicationOpenURLOptionsKey.sourceApplication]
if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication as? String) ?? false {
return true
}
return false
}
}
And here is the subclass I created:
class FUICustomAuthPickerViewController: FUIAuthPickerViewController,FUIAuthDelegate {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .black
}
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
return FUICustomAuthPickerViewController(nibName: "FUICustomAuthPickerViewController",
bundle: Bundle.main,
authUI: authUI)
}
}
On the Firebase documentation for customization, they say that:
You can customize the sign-in screens by subclassing FirebaseUI's view controllers and specifying them in FUIAuth's delegate methods.
I am a beginner, how can I do that?
Edited:
So by following the instructions on this link I managed to add stuff to the pre-built UI by creating an extension to the FUIAuthDelegate.
extension LoginViewController:FUIAuthDelegate {
func application(_ app: UIApplication, open url: URL,
options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
let sourceApplication = options[UIApplicationOpenURLOptionsKey.sourceApplication]
if FUIAuth.defaultAuthUI()?.handleOpen(url, sourceApplication: sourceApplication as? String) ?? false {
return true
}
return false
}
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
let vc = FUIAuthPickerViewController(authUI: authUI)
let view = UIView(frame: .zero)
view.backgroundColor = .black
view.translatesAutoresizingMaskIntoConstraints = false
vc.view.addSubview(view)
NSLayoutConstraint.activate([
view.heightAnchor.constraint(equalTo: vc.view.heightAnchor, multiplier: 1),
view.widthAnchor.constraint(equalTo: vc.view.widthAnchor, multiplier: 1)])
return vc
}
}
Turns out subclass is not necessarily needed. However, I can't seem to make this view I created to be the background, it either covers everything or nothing at all.I tried changing the background color of the view directly, didn't work. Anyone knows how to do this?
Solved the problem using the method and one of the comments provided in this link. Turns out, apart from subclassing, you have to add the following two methods to your subclass for it to work.
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, authUI: FUIAuth?) {
super.init(nibName: nil, bundle: Bundle.main, authUI: authUI!)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
However, the approach I used (not sure if other approaches raise the same problem) also caused another problem - the email sign-in button stopped responding - which is addressed in this link by presenting the view controller with a navigationViewController because the email sign-in button works together with the navigation bar, you can get rid of it once you have presented the view with a navigationViewController.
Now the complete subclass looks like this:
import UIKit
import FirebaseUI
class FUICustomAuthPickerViewController: FUIAuthPickerViewController {
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?, authUI: FUIAuth?) {
super.init(nibName: nil, bundle: Bundle.main, authUI: authUI!)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func viewDidLoad() {
super.viewDidLoad()
let width = UIScreen.main.bounds.size.width
let height = UIScreen.main.bounds.size.height
let imageViewBackground = UIImageView(frame: CGRect(x: 0, y: 0, width: width, height: height))
imageViewBackground.backgroundColor = .eatstrOrange
view.insertSubview(imageViewBackground, at: 0)
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
navigationController?.setNavigationBarHidden(true, animated: animated)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
navigationController?.setNavigationBarHidden(false, animated: animated)
}
}
And here's the main view controller:
import UIKit
import FirebaseUI
class LoginViewController: UIViewController{
override func viewDidLoad() {
super.viewDidLoad()
let authUI = FUIAuth.defaultAuthUI()
let delegate = authUI?.delegate
authUI?.delegate = delegate
let providers: [FUIAuthProvider] = [
FUIGoogleAuth(),
FUIEmailAuth(),
FUIPhoneAuth(authUI:FUIAuth.defaultAuthUI()!)]
authUI?.providers = providers
let authViewController = FUICustomAuthPickerViewController(authUI: authUI!)
authViewController.modalPresentationStyle = .fullScreen
navigationController?.pushViewController(authViewController, animated: false)
}
}
Final outcome

Storyboard doesn't contain a view controller with identifier error?

I'm trying to present a programmatically made viewcontroller on a viewcontroller, where I can't figure out how to make ID of such made-up viewcontroller.
As can be seen in the code under, I have a base view controller, 'ViewController' and if I click a button(didTapButton) I want a programmatically made view controller(SecondViewController) show up.
Though I can't set the second view controller's name, that I can't even execute the code -- instantiateViewController(withIdentifier: "SecondController").
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func didTapButton(_ sender: Any) {
let controller = storyboard!.instantiateViewController(withIdentifier: "SecondController")
present(controller, animated: true)
}
}
......
class SecondViewController: UIViewController {
private var customTransitioningDelegate = TransitioningDelegate()
override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(nibName: "SecondController", bundle: nibBundleOrNil)
configure()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
configure()
}
}
How can I set up the second view controller's ID? If it's not what should be done, what else can I try?
instantiateViewController lets you instanciate something that is defined in a given storyboard. So either you name it in the storyboard via xcode or you must do something else. For example, instanciate the object from code, ie let c=SecondViewController() (with appropriate parameters). You are trying to mix different ways to instanciate an object.
You don't need any identifiers for programmatically created vcs just do
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .green
}
}
Use like
#IBAction func didTapButton(_ sender: Any) {
let vc = SecondViewController()
present(vc, animated: true)
}
Edit:
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
self.view.backgroundColor = .green
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
let vc2 = SecondViewController()
vc2.providesPresentationContextTransitionStyle = true
vc2.definesPresentationContext = true
vc2.modalPresentationStyle = .overCurrentContext
self.present(vc2, animated: true)
}
}
}
class SecondViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let v = UIView()
v.backgroundColor = .red
view.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
v.centerXAnchor.constraint(equalTo: self.view.centerXAnchor),
v.centerYAnchor.constraint(equalTo: self.view.centerYAnchor),
v.widthAnchor.constraint(equalToConstant:200),
v.heightAnchor.constraint(equalToConstant:200)
])
}
}

Xib file renders when called by my page view controller but not as a tab

I'm rendering a Xib file which is a subview in a view controller, the view controller is called by setViewController in a pageviewcontroller . It works great. I then placed the same Xib sub view in a view controller called by my tab view controller and it doesn't render.
To try and get to the bottom of this weird occurrence I have removed all code such that there is no class attached to the UIviewcontroller parent of the subview Xib in both cases. It is now a pure IB implementation of the Xib and the Xib classes are identical.
So in my comparison between the two implementations the only difference is one is opened from a page view controller and the other from a tab. I even copied the working view controller just so I knew they were identical.
Has anyone had this experience (where the same Xib doesn't render in one workflow compared to another) or have an idea as to why the two implementations cause different results??
Update: I took it one step further. Now there is absolutely no code. The only thing that is in the Xib is an unattached label. It only renders when the parent view is called from a page view controller! Wtf
Signin View Xib. (Works when instantiated from UIPageViewController:
SigninView.swift
class SigninView: UIView, GIDSignInUIDelegate, GIDSignInDelegate {
var view: UIView!
var nibName: String = "SigninView"
var delegate: SigninViewDelegate?
#IBOutlet weak var googleButton: GIDSignInButton!
#IBOutlet weak var signoutButton: UIButton!
override init(frame: CGRect) {
// properties
super.init(frame: frame)
// Set anything that uses the view or visible bounds
setup()
}
override func awakeFromNib() {
print("AWAKING!")
}
#IBAction func tapSignOut(sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
UserManager().logout()
self.updateDisplay()
}
required init?(coder aDecoder: NSCoder) {
// properties
super.init(coder: aDecoder)
// Setup
setup()
}
func setup() {
view = loadViewFromNib()
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().delegate = self
view.frame = bounds
view.autoresizingMask = [UIViewAutoresizing.FlexibleWidth, UIViewAutoresizing.FlexibleHeight]
addSubview(view)
self.updateDisplay()
}
func updateDisplay() {
self.googleButton.layer.hidden = false
}
func loadViewFromNib() -> UIView {
let bundle = NSBundle(forClass: self.dynamicType)
let nib = UINib(nibName: nibName, bundle: bundle)
let view = nib.instantiateWithOwner(self, options: nil)[0] as! UIView
return view
}
func signIn(signIn: GIDSignIn!, presentViewController viewController: UIViewController!) {
//present vc on delegate
let delegateVC = self.delegate as! UIViewController
delegateVC.presentViewController(viewController, animated: true, completion: nil)
}
func signIn(signIn: GIDSignIn!, dismissViewController viewController: UIViewController!) {
print("Dismiss view controller")
let delegateVC = self.delegate as! UIViewController
delegateVC.dismissViewControllerAnimated(true, completion: nil)
self.updateDisplay()
}
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
var success = false
if (user != nil) {
print("signed in? UID: \(user.userID), email: \(user.profile.email), name: \(user.profile.name), pic: \(user.profile.imageURLWithDimension(150))")
UserManager().createUser(user)
LocalDataStorage().saveContext()
success = true
} else {
print("User not signed in.")
}
self.updateDisplay()
if self.delegate != nil && self.delegate!.signInComplete != nil {
self.delegate!.signInComplete!(success)
}
}
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user: GIDGoogleUser!, withError error: NSError!) {
print("disconnected")
}
}
Working UIViewController that contains working Signin Subview
Working call from UIPageViewController
let vc = UIStoryboard(name: "Intro", bundle: nil).instantiateViewControllerWithIdentifier("IntroPage2")
setViewControllers([getSlide(index)],direction: .Forward,animated: true,completion: nil)
UIViewController called from tab view that does not render signin subview
I am posting AN answer but really it's more of a work around. After hours of trying to finagle the views I get the code working consistently. To get it working created a new Xib and new VC file for the Signin module. I copied all the code from my original Signin class to the new one and it worked. No code was altered.
So if anyone comes across the gnarly painful situation give that a shot! This could very well be an XCode bug.