UINavigationBar bar height is not changing - swift

I have a custom UINavigationController which I Simply resize the height but I get no result:
actually my main code is this: ( this will get perfect height according tu device size )
class NavigationController1: UINavigationController {
let Theframe = UIScreen.main.bounds
override func awakeFromNib() {
self.navigationBar.frame = CGRect.init(x: self.navigationBar.frame.origin.x*Theframe.width/375, y: self.navigationBar.frame.origin.y*Theframe.height/667, width:self.navigationBar.frame.width*Theframe.width/375, height: self.navigationBar.frame.height*Theframe.height/667)
}
}
but the upper code for height isn't working neither.

I solved the issue by putting in viewWillLayoutSubviews()
final code:
import UIKit
class NavigationController1: UINavigationController {
let Theframe = UIScreen.main.bounds
override func viewWillLayoutSubviews() {
self.navigationBar.frame = CGRect.init(x: self.navigationBar.frame.origin.x, y: self.navigationBar.frame.origin.y, width:self.navigationBar.frame.width, height: 200)
}
}

It could be override and redrawn because of the sIze inspector. Put the same requirements there so you can ensure its not overriding the custom code.

Related

Identifying Objects in Firebase PreBuilt UI in Swift

FirebaseUI has a nice pre-buit UI for Swift. I'm trying to position an image view above the login buttons on the bottom. In the example below, the imageView is the "Hackathon" logo. Any logo should be able to show in this, if it's called "logo", since this shows the image as aspectFit.
According to the Firebase docs page:
https://firebase.google.com/docs/auth/ios/firebaseui
You can customize the signin screen with this function:
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
return FUICustomAuthPickerViewController(nibName: "FUICustomAuthPickerViewController",
bundle: Bundle.main,
authUI: authUI)
}
Using this code & poking around with subviews in the debuggers, I've been able to identify and color code views in the image below. Unfortunately, I don't think that the "true" size of these subview frames is set until the view controller presents, so trying to access the frame size inside these functions won't give me dimensions that I can use for creating a new imageView to hold a log. Plus accessing the views with hard-coded index values like I've done below, seems like a pretty bad idea, esp. given that Google has already changed the Pre-Built UI once, adding a scroll view & breaking the code of anyone who set the pre-built UI's background color.
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
// Create an instance of the FirebaseAuth login view controller
let loginViewController = FUIAuthPickerViewController(authUI: authUI)
// Set background color to white
loginViewController.view.backgroundColor = UIColor.white
loginViewController.view.subviews[0].backgroundColor = UIColor.blue
loginViewController.view.subviews[0].subviews[0].backgroundColor = UIColor.red
loginViewController.view.subviews[0].subviews[0].tag = 999
return loginViewController
}
I did get this to work by adding a tag (999), then in the completion handler when presenting the loginViewController I hunt down tag 999 and call a function to add an imageView with a logo:
present(loginViewController, animated: true) {
if let foundView = loginViewController.view.viewWithTag(999) {
let height = foundView.frame.height
print("FOUND HEIGHT: \(height)")
self.addLogo(loginViewController: loginViewController, height: height)
}
}
func addLogo(loginViewController: UINavigationController, height: CGFloat) {
let logoFrame = CGRect(x: 0 + logoInsets, y: self.view.safeAreaInsets.top + logoInsets, width: loginViewController.view.frame.width - (logoInsets * 2), height: self.view.frame.height - height - (logoInsets * 2))
// Create the UIImageView using the frame created above & add the "logo" image
let logoImageView = UIImageView(frame: logoFrame)
logoImageView.image = UIImage(named: "logo")
logoImageView.contentMode = .scaleAspectFit // Set imageView to Aspect Fit
// loginViewController.view.addSubview(logoImageView) // Add ImageView to the login controller's main view
loginViewController.view.addSubview(logoImageView)
}
But again, this doesn't seem safe. Is there a "safe" way to deconstruct this UI to identify the size of this button box at the bottom of the view controller (this size will vary if there are multiple login methods supported, such as Facebook, Apple, E-mail)? If I can do that in a way that avoids the hard-coding approach, above, then I think I can reliably use the dimensions of this button box to determine how much space is left in the rest of the view controller when adding an appropriately sized ImageView. Thanks!
John
This should address the issue - allowing a logo to be reliably placed above the prebuilt UI login buttons buttons + avoiding hard-coding the index values or subview locations. It should also allow for properly setting background color (also complicated when Firebase added the scroll view + login button subview).
To use: Create a subclass of FUIAuthDelegate to hold a custom view controller for the prebuilt Firebase UI.
The code will show the logo at full screen behind the buttons if there isn't a scroll view or if the class's private constant fullScreenLogo is set to false.
If both of these conditions aren't meant, the logo will show inset taking into account the class's private logoInsets constant and the safeAreaInsets. The scrollView views are set to clear so that a background image can be set, as well via the private let backgroundColor.
Call it in any signIn function you might have, after setting authUI.providers. Call would be something like this:
let loginViewController = CustomLoginScreen(authUI: authUI!)
let loginNavigationController = UINavigationController(rootViewController: loginViewController)
loginNavigationController.modalPresentationStyle = .fullScreen
present(loginNavigationController, animated: true, completion: nil)
And here's one version of the subclass:
class CustomLoginScreen: FUIAuthPickerViewController {
private var fullScreenLogo = false // false if you want logo just above login buttons
private var viewContainsButton = false
private var buttonViewHeight: CGFloat = 0.0
private let logoInsets: CGFloat = 16
private let backgroundColor = UIColor.white
private var scrollView: UIScrollView?
private var viewContainingButton: UIView?
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
// set color of scrollView and Button view inside scrollView to clear in viewWillAppear to avoid a "color flash" when the pre-built login UI first appears
self.view.backgroundColor = UIColor.white
guard let foundScrollView = returnScrollView() else {
print("😡 Couldn't get a scrollView.")
return
}
scrollView = foundScrollView
scrollView!.backgroundColor = UIColor.clear
guard let foundViewContainingButton = returnButtonView() else {
print("😡 No views in the scrollView contain buttons.")
return
}
viewContainingButton = foundViewContainingButton
viewContainingButton!.backgroundColor = UIColor.clear
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// Create the UIImageView at full screen, considering logoInsets + safeAreaInsets
let x = logoInsets
let y = view.safeAreaInsets.top + logoInsets
let width = view.frame.width - (logoInsets * 2)
let height = view.frame.height - (view.safeAreaInsets.top + view.safeAreaInsets.bottom + (logoInsets * 2))
var frame = CGRect(x: x, y: y, width: width, height: height)
let logoImageView = UIImageView(frame: frame)
logoImageView.image = UIImage(named: "logo")
logoImageView.contentMode = .scaleAspectFit // Set imageView to Aspect Fit
logoImageView.alpha = 0.0
// Only proceed with customizing the pre-built UI if you found a scrollView or you don't want a full-screen logo.
guard scrollView != nil && !fullScreenLogo else {
print("No scrollView found.")
UIView.animate(withDuration: 0.25, animations: {logoImageView.alpha = 1.0})
self.view.addSubview(logoImageView)
self.view.sendSubviewToBack(logoImageView) // otherwise logo is on top of buttons
return
}
// update the logoImageView's frame height to subtract the height of the subview containing buttons. This way the buttons won't be on top of the logoImageView
frame = CGRect(x: x, y: y, width: width, height: height - (viewContainingButton?.frame.height ?? 0.0))
logoImageView.frame = frame
self.view.addSubview(logoImageView)
UIView.animate(withDuration: 0.25, animations: {logoImageView.alpha = 1.0})
}
private func returnScrollView() -> UIScrollView? {
var scrollViewToReturn: UIScrollView?
if self.view.subviews.count > 0 {
for subview in self.view.subviews {
if subview is UIScrollView {
scrollViewToReturn = subview as? UIScrollView
}
}
}
return scrollViewToReturn
}
private func returnButtonView() -> UIView? {
var viewContainingButton: UIView?
for view in scrollView!.subviews {
viewHasButton(view)
if viewContainsButton {
viewContainingButton = view
break
}
}
return viewContainingButton
}
private func viewHasButton(_ view: UIView) {
if view is UIButton {
viewContainsButton = true
} else if view.subviews.count > 0 {
view.subviews.forEach({viewHasButton($0)})
}
}
}
Hope this helps any who have been frustrated trying to configure the Firebase pre-built UI in Swift.

How to fix the wrong frame after device rotation?

I use custom alert view in my application. When called, its frame completely fills the screen. However, after the device rotates, it does not look right.
Then I try to change my alert frame and assign it a new screen size in viewWillTransition
override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
coordinator.animate(alongsideTransition: nil) { _ in UIView.setAnimationsEnabled(true) }
UIView.setAnimationsEnabled(false)
super.viewWillTransition(to: size, with: coordinator)
if let _ = alert {
alert?.rotateAlert(to: size)
Logger.Log("after size = \(alert?.backgroundView.frame.size)")
Logger.Log("after frame = \(alert?.frame)")
Logger.Log("after bounds = \(alert?.bounds)")
}
I tried to do the same in viewDidLayoutSubviews
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let _ = alert {
Logger.Log("viewDidLayoutSubviews before rotation")
alert?.rotateAlert(to: self.view.frame.size)
Logger.Log("after size = \(alert?.backgroundView.frame.size)")
Logger.Log("after frame = \(alert?.frame)")
Logger.Log("after bounds = \(alert?.bounds)")
}
}
func rotateAlert(to size: CGSize) {
self.frame.size = size
backgroundView.frame = frame
}
Thus the frame of my alert changes. But its size is still smaller than the screen in height, thus forming an empty area.
Why is this happening? And how do I fix this? (I use Swift4)
To place your alert in the center of the screen, assuming that it is an instance of UIView, do this:
alert.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true
alert.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true
// Or
NSLayoutConstraint.activate([
alert.centerXAnchor.constraint(equalTo: view.centerXAnchor),
alert.centerYAnchor.constraint(equalTo: view.centerYAnchor),
])
This is using auto layout to position your views, and the views will properly update when the trait collection changes.
When adding a view programmatically the view doesn't adjust according to the screen rotation because you have fixed the view height, width so after rotation you need to again give its frame.
This doesn't happen when you use constraints to add a view either programmatically or from the storyboard
For your alert to resize you have to again give its frame in viewDidLayoutSubviews()
override func viewDidLayoutSubviews() {
YourAlertView.frame = CGRect(x:0 , y: 0,width: self.view.frame.width, height: self.view.frame.height )
}
Works like a charm for me.

Why doesn't overriding `intrinsicContentSize` in `NSView` work for `NSScrollView`?

I have placed an instance of custom class BigView that is a subclass of NSView inside a NSScrollView in IB. The content size of my BigView will be computed at runtime. What is the best practices for setting the content size?
Overriding intrinsicContentSize, as suggested in the The Big Nerd Ranch guide, does not seem to work -- the frame remains at its original size:
class BigView: NSView {
...
override var intrinsicContentSize: NSSize { // doesn't work?!
return NSSize(width: 10000, height: 10000)
}
...
}
Setting the frame programmatically (or in IB) does work:
class BigView: NSView {
...
override func awakeFromNib() {
...
self.frame = NSRect(x: 0, y: 0, width: 10000, height: 10000)
}
...
}I
or from the controller:
class ViewController: NSViewController {
#IBOutlet weak var bigView: BigView!
...
override func viewDidLoad() {
super.viewDidLoad()
bigView.frame = NSRect(x: 0, y: 0, width: 1000, height: 1000)
}
...
}
This can also be done throughout the scrollview's documentView property:
class ViewController: NSViewController {
#IBOutlet weak var scrollView: NSScrollView!
...
override func viewDidLoad() {
super.viewDidLoad()
scrollView.documentView?.frame = NSRect(x: 0, y: 0,
width: 1000, height: 1000)
}
...
}
I suppose one could also use AutoLayout constraints as well.
What doesn't overriding intrinsicContentSize work?
The problem is that when translatesAutoresizingMaskIntoConstraints is enabled in newer versions of AppKit default constraints are set without considering the intrinsic size.
To fix this you need to add the constraints manually. First pin the position of your BigView to the top left of the superview (Should be the NSClipView). Then go to the Size Inspector, and in the Content Compression Resistance Priority section set Intrinsic Size to Placeholder. That gives your view the constraints it needs and everything should work at runtime.

Can I add a UIImageView to an UIInputViewController in Swift?

I'm working on a custom keyboard and want to add some UIImageViews to the interface. I can load a .Xib fine as shown below, but when I try to create a UIImage within the main view it causes it to crash. Do you know why this is?
import UIKit
class KeyboardViewController: UIInputViewController {
override func updateViewConstraints() {
super.updateViewConstraints()
}
override func viewDidLoad() {
super.viewDidLoad()
let myView = NSBundle.mainBundle().loadNibNamed("KeyboardView", owner: nil, options: nil)[0] as UIView
self.view.addSubview(myView)
let image = UIImage(named: "grid.png")
let imageSize = CGSize(width: 216, height: 216)
var gridImageView = UIImageView(frame: CGRect(origin: CGPoint(x: 0, y: 0), size: imageSize))
gridImageView.image = image
//tried this- Crashes
self.addSubview(gridImageView)
//also tried this- Crashes
self.view.insertSubview(view: gridImageView, aboveSubview: myView)
}
If I manually add a ImageView to the .XIB, it also crashes. I feel like UIInputViewController will not let you use a Image View, even though the documentation would indicate it's possible. I'm usually an idiot with these things so am probably misunderstanding the docs:
https://developer.apple.com/library/prerelease/ios/documentation/UIKit/Reference/UIInputViewController_Class/index.html#//apple_ref/occ/instp/UIInputViewController/inputView

create scrollview programmatically in swift without outlet

I have tried enough but i don't understand what is wrong with code, it was working fine when I created an outlet, but then I found out that i don't need an outlet, so want to create it just programmatically.
var BestPractices = UIScrollView(frame: CGRectMake(0, 94, 768, 924))
BestPractices.hidden = true
I cannot access the properties of "BestPractices" in viewController , while same code works fine in playground
This is an answer from this question.
.
class ScrollingViewController : UIViewController {
// Create a scrollView property that we'll set as our view in -loadView
let scrollView = UIScrollView(frame: UIScreen.mainScreen().bounds)
override func loadView() {
// calling self.view later on will return a UIView!, but we can simply call
// self.scrollView to adjust properties of the scroll view:
self.view = self.scrollView
// setup the scroll view
self.scrollView.contentSize = CGSize(width:1234, height: 5678)
// etc...
}
func example() {
let sampleSubView = UIView()
self.view.addSubview(sampleSubView) // adds to the scroll view
// cannot do this:
// self.view.contentOffset = CGPoint(x: 10, y: 20)
// so instead we do this:
self.scrollView.contentOffset = CGPoint(x: 10, y: 20)
}
add this line to identify scroll bar at last of loadview().
scrollView.backgroundColor = UIColor.blue