Firebaseui-ios: Background view not visible after subclassing FUIAuthPickerViewController

Created on 23 Feb 2019  路  19Comments  路  Source: firebase/FirebaseUI-iOS

What

I subclassed FUIAuthPickerViewController in order to add a background image (UIImageView) behind the Google / FB sign-in buttons but I can't get it to display. This started happening after updating Firebase pods to the latest version (was working fine on the previous versions). See Details section for info.

Env

  • XCode 10.1
  • Swift 4.2
  • iOS 12.1.4
  • CocoaPods 1.6.0

Details

Issue description, code snippets, pod & sdk versions & screenshots on StackOverflow, since I initially didn't think it could be a bug.

auth

Most helpful comment

@polis80cy try this workaround:

override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = view.subviews[0]
        scrollView.backgroundColor = .clear
        let contentView = scrollView.subviews[0]
        contentView.backgroundColor = .clear

        let background = UIImage(named: "image")
        backgroundImageView = UIImageView(image: background)
        backgroundImageView.contentMode = .scaleToFill
        view.insertSubview(backgroundImageView, at: 0)
}

And set backgroundImageView's constraints to the superview's edges. It worked in my case.

All 19 comments

If you print out the view hierarchy of the auth picker controller's view, what does it look like?

Background seems to be hiding in the back, behind a scrollview(?);

screenshot 2019-02-25 at 9 19 17 pm

screenshot 2019-02-25 at 9 23 01 pm

Ah, that sounds like it was introduced in https://github.com/firebase/FirebaseUI-iOS/pull/590. We can fix this by exposing the scroll view publicly so subclasses can add and remove subviews as they desire.

Sounds good! Will this come out in the next release?

If you are creating a subview and inserting it, you can also insert it at 1 instead of 0 which is a temporary fix until the scrollview is explicitly exposed.

 view.insertSubview(subview, at: 1) }

Tried that; I can see the background but not the sign in buttons. See my SO link for a screenshot.

Sorry, my use case was slightly different. I think if you do view.subviews[0].insertSubview(subview, at: 0), that should work. I also needed to set the background to clear as well for the UI View that houses the auth buttons.

 let selectorView = view.subviews[0].subviews[0]
 selectorView.backgroundColor = UIColor.clear

Before:
image

After:
image

Thank you, this actually works as a temp fix;

self.view.subviews[0].subviews[0].backgroundColor = UIColor.clear
self.view.subviews[0].insertSubview(imgView, at: 0)

The top area of the scrollbarview is still visible though.

Any thoughts? I initially thought it was the status bar.

Screenshot 2019-03-11 at 3 24 20 PM
Screenshot 2019-03-11 at 3 23 33 PM
Screenshot 2019-03-11 at 3 30 05 PM

@polis80cy The new auth picker selector has a scrollview while previous version doesn't. The scrollview is added because some apps cannot display all their sign in options (such as horizontal mode). We added the scroll view with backward compatibility in mind, and should be able to make most of developers' custom views work. But in case a developer is doing something we didn't expect (such as utilizing some internal behaviors of our previous code), it may cause layout issues.

In your case, as far as I understand, you're trying to make a custom view with a background image. I believe you are fully able to do that with the new version. In our sample app, we have a FUICustomAuthPickerViewController (and you can test that by using the Demo app, and turn "Custom Authorization UI" switch on in the setting section). That view has a "knobColor" background.

To do add an image background manually, you can add the following code to the custom class (I closely mimics your swift code):

@implementation FUICustomAuthPickerViewController {
  UIView * _backgroundView;
}

- (void)viewDidLoad {
  [super viewDidLoad];
  _backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
  _backgroundView.layer.contents = (id)[UIImage imageNamed:@"img.png"].CGImage;
  [self.view insertSubview:_backgroundView atIndex:0];
}

- (void)viewDidLayoutSubviews {
  [super viewDidLayoutSubviews];
  _backgroundView.frame = self.view.bounds;
}

I just tested and it correctly renders the image as the custom background.

Simulator Screen Shot - iPhone X - 2019-03-18 at 12 33 53

Perhaps your custom xib file is different and use some assumptions from the previous version, but with the vanilla xib file from the sample app, and the above ObjC code, I don't see any issue with the layout.

@Yue-Wang-Google I tried that and it still doesn't work I'm afraid.

This is on the latest version and all I'm doing is subclassing FUIAuthPickerViewController as per the readme file on GH. I'm not utilizing anything from previous codebase versions.

It's in Swift, no custom xib files.

Trying your suggestion:

class AuthViewController: FUIAuthPickerViewController {
    var backgView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let backgroundImage = #imageLiteral(resourceName: "MainBackground")
        backgView = UIView(frame: CGRect.zero)
        backgView.layer.contents = backgroundImage.cgImage
        self.view.insertSubview(backgView, at: 0)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()

        backgView.frame = self.view.bounds
    }
}

And this is my delegate (which triggers the above class to fire) in AppDelegate.swift:

func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
        return AuthViewController(authUI: authUI)
}

The result:
Simulator Screen Shot - iPhone XR - 2019-03-20 at 11 02 20


Trying the little hack as suggested by @cwoolum works but leaves that top grey scrollbar area visible as you can see from the screenshots in my previous comment. This is how:

class AuthViewController: FUIAuthPickerViewController {
    var imgView: UIImageView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let backgroundImage = #imageLiteral(resourceName: "MainBackground")
        imgView = UIImageView(image: backgroundImage)
        imgView.contentMode = .scaleAspectFill

        self.view.subviews[0].subviews[0].backgroundColor = UIColor.clear
        self.view.subviews[0].insertSubview(imgView, at: 0)
    }

    override func viewDidLayoutSubviews() {
        super.viewDidLayoutSubviews()
        imgView.frame = self.view.bounds
    }
} 

And this is the result (you can see the grey top part):
Simulator Screen Shot - iPhone XR - 2019-03-20 at 11 15 17

If there's a way to get rid of that grey top part (I've added some screenshots about that in my previous comment), I can definitely live with this. That's all I need at this point.

@polis80cy please try the samples in /samples/objc/FirebaseUI-demo-objc/Samples/Auth/FUICustomAuthPickerViewController.{xib,h,m}. They should work well.

I tried the sample code with the latest FirebaseUI (6.2.1), without custom .xib file:

@implementation FUICustomAuthPickerViewController {
  UIView * _backgroundView;
}

- (void)viewDidLoad {
  [super viewDidLoad];
  _backgroundView = [[UIView alloc] initWithFrame:CGRectZero];
  _backgroundView.layer.contents = (id)[UIImage imageNamed:@"img.png"].CGImage;
  [self.view insertSubview:_backgroundView atIndex:0];
}

- (void)viewDidLayoutSubviews {
  [super viewDidLayoutSubviews];
  _backgroundView.frame = self.view.bounds;
}

And still does not work. The _backgroundView is behind the _scrollView.

@polis80cy try this workaround:

override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = view.subviews[0]
        scrollView.backgroundColor = .clear
        let contentView = scrollView.subviews[0]
        contentView.backgroundColor = .clear

        let background = UIImage(named: "image")
        backgroundImageView = UIImageView(image: background)
        backgroundImageView.contentMode = .scaleToFill
        view.insertSubview(backgroundImageView, at: 0)
}

And set backgroundImageView's constraints to the superview's edges. It worked in my case.

@zhanetpaneva this works beautifully, thank you!

@polis80cy try this workaround:

override func viewDidLoad() {
        super.viewDidLoad()

        let scrollView = view.subviews[0]
        scrollView.backgroundColor = .clear
        let contentView = scrollView.subviews[0]
        contentView.backgroundColor = .clear

        let background = UIImage(named: "image")
        backgroundImageView = UIImageView(image: background)
        backgroundImageView.contentMode = .scaleToFill
        view.insertSubview(backgroundImageView, at: 0)
}

And set backgroundImageView's constraints to the superview's edges. It worked in my case.

this caused error for me when cancling on email authui screen

Does anyone know how to make it full screen?

Even If I try to make the Nav Transparent the image doesn't cover the UIScreen.main.bounds.height of the screen, making it cut at the navBar.

I got this:

at LoginVC
/// Delegate function that allows us to create a custom FUIAuthPickerViewController
///
/// - Parameter authUI: FUIAuth instance
/// - Returns: The custom FUIAuthPickerViewController
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
// Create an instance of the FirebaseAuth login view controller
let customLoginViewController = FUIAuthPickerViewController(authUI: authUI)

    customLoginViewController.navigationItem.leftBarButtonItem = nil

    // Use the previous values to build a CGRect for the drawable's view's frame
    let drawableFrame = CGRect(x: self.view.frame.origin.x, y: self.view.frame.origin.y, width: self.view.frame.width, height: UIScreen.main.bounds.height)

    let drawableImageView = UIImageView(frame: drawableFrame)
    drawableImageView.image = UIImage(named: "Logging")
    drawableImageView.contentMode = .scaleAspectFill

    customLoginViewController.view.subviews[0].subviews[0].backgroundColor = UIColor.clear
    customLoginViewController.view.subviews[0].insertSubview(drawableImageView, at: 0)

    return customLoginViewController
}

This at AppDelegate:

didFinishLaunchingWithOptions.... {

window?.rootViewController = LoginViewController()

    // Remove the bottom line for the UINavigationBar
    UINavigationBar.appearance().shadowImage = UIImage()
    UINavigationBar.appearance().setBackgroundImage(UIImage(), for: .default)


    //Clear NavBar for a fullscreen ImageView
    UINavigationBar.appearance().isTranslucent = true

}

Screen Shot 2019-09-17 at 2 43 38 PM

IMG_497F2BBDF059-1

How I disable the scrolling or the bounce? how can I remove the navBar title?

I have tried this code

/// Delegate function that allows us to create a custom FUIAuthPickerViewController
///
/// - Parameter authUI: FUIAuth instance
/// - Returns: The custom FUIAuthPickerViewController

    func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
        // Create an instance of the FirebaseAuth login view controller
        let customLoginVC = FUIAuthPickerViewController(authUI: authUI)

        customLoginVC.navigationItem.leftBarButtonItem = nil

        customLoginVC.navigationController?.isToolbarHidden = true
        customLoginVC.navigationController?.isNavigationBarHidden = true


        //Original Frame
        let drawableFrame = CGRect(x: 0, y: -44, width: UIScreen.main.bounds.width, height: UIScreen.main.bounds.height)


        let drawableImageView = UIImageView(frame: drawableFrame)
        drawableImageView.image = UIImage(named: "pdf2")
        drawableImageView.contentMode = .scaleAspectFill

        //vc
        customLoginVC.view.subviews[0].subviews[0].backgroundColor = UIColor.clear
        customLoginVC.view.subviews[0].insertSubview(drawableImageView, at: 0)

        customLoginVC.view.backgroundColor = .black


        let statusBar = UIApplication.shared.value(forKeyPath: "statusBarWindow.statusBar") as? UIView
        statusBar?.backgroundColor = UIColor.clear

        return customLoginVC
    }

Unfortunately, toolBar is not hidden, navBar is not hidding or setting it's text even if I set another title e.g. navBar.title = "hi" or even nil. background also doesn't change and it is white.

The unique thing that I'm able to modify is the back button which I'm already setting in up as nil and it works but the remaning code it is just like ignoring it... any thoughts? I'm all ears.

Was this page helpful?
0 / 5 - 0 ratings