Star Rating Control in UIAlertController subview is clipped when centred - Swift - swift

I am using Cosmos Star Rating Control and I am embedding it as a subview of a UIAlertController. I struggled to get this to look correctly, however I managed to get the look I was going for with it being centred and all, but now I believe the view for the Star Control is being clipped, as it does not allow selection of certain areas on certain stars, also when turning on clipToBounds = true you can blatantly see that it's being clipped. I am just not sure why or how to solve this. The code and some screenshots are below. I also tried using a custom alert controller that allows you to add a custom content view, but still suffered the same issue.
#IBAction func rateButtonClicked(sender: AnyObject?) {
//Alert for the rating
let alert = UIAlertController(title: "\n\n", message: "", preferredStyle: UIAlertControllerStyle.ActionSheet)
let customView = UIView(frame: CGRect(x: 0, y: 0, width: alert.view.frame.width, height: alert.view.frame.height))
//The x/y coordinate of the rating view
let xCoord = alert.view.frame.width/2 - 95 //(5 starts multiplied by 30 each, plus a 5 margin each / 2)
let yCoord = CGFloat(25.0)
ratingView.rating = 0.0
ratingView.settings.starSize = 30
ratingView.settings.emptyBorderColor = UIColor.blackColor()
ratingView.settings.updateOnTouch = true
ratingView.frame.origin.x = xCoord
ratingView.frame.origin.y = yCoord
customView.addSubview(ratingView)
alert.addAction(UIAlertAction(title: "Save Rating", style: UIAlertActionStyle.Default, handler: ratingCompletionHandler))
alert.addAction(UIAlertAction(title: "Cancel", style: UIAlertActionStyle.Destructive, handler: nil))
alert.view.addSubview(customView)
dispatch_async(dispatch_get_main_queue(), {
self.presentViewController(alert, animated: true, completion: nil)
})
}
Image with clipToBounds Turned on:
Image with clipToBounds Turned off:

After some playing around, I noticed that the CosmosView was using intrinsic content size and only would be as big as the content it holds(the stars), which I think was causing the issue, so a bit of an ugly work around I used, that works for now, was just to assign it a new frame of a custom size like so:
//Make a custom frame
ratingView.frame = CGRectMake(0, 0, 200.0, 60.0)
ratingView.frame.origin.x = xCoord
ratingView.frame.origin.y = yCoord
This still has the stars centred as above, but now they are all clickable and provide the correct rating value when clicked.

Try this simple way that may help you:
let customView = UIView(frame: CGRect(x: 0, y: 0, width: alert.view.frame.width, height: alert.view.frame.height))
//add this line here
customView.sizeToFit()
//The x/y coordinate of the rating view
let xCoord = customView.view.frame.width/2 - 95 //(5 starts multiplied by 30 each, plus a 5 margin each / 2)
let yCoord = CGFloat(25.0)

If preferredStyle is .alert and not .actionSheet. Based on the fact that we cannot legitimately know the sizes before the alert controller (view) is shown, I'd opt to do this (in Swift 3):
DispatchQueue.main.async {
self.present(alert, animated: true, completion: {
if let sv = alert.view.subviews[0] {
let w = ratingView.intrinsicContentSize.width
let h = ratingView.intrinsicContentSize.height
ratingView.frame = CGRect(x: sv.bounds.width/2 - w/2, y: sv.bounds.height/2 - h/2, width: w, height: h)
sv.addSubview(ratingView)
}
})
}
Note: Might have to make adjustments for .actionSheet.

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.

iOS Swift - Position subview based on button frame (button is inside Vertical Stack View)

Update
After adding convert method between rect and main View, the Y position is ok, but X coordinate is shifted to the right outside of the main view:
Dropdown view(subview) is off main view
Below is button frame before and after convert method. Main view is 414 x 896. Dropdown menu somehow shifts to the right as on attached image.
button frame in stackView btnRect: (120.66666666666666, 0.0, 293.3333333333333, 30.0)
button frame in main view: cvtRect (241.33333333333331, 190.0, 293.3333333333333, 30.0)
view: Optional(>)
Goal. I want to make a dropdown list by showing UIView with dropdown options below a button. Button is inside of Vertical Stack View. I add a TableView with dropdown options to this dropdown UIView.I want to click a button and have this UIView with TableView inside to show just below the button. Basically following this tutorial https://www.youtube.com/watch?v=D3DCPaEE4hQ with the exception that my button is inside of Vertical Stack View.
Issue. UIView and TableView inside UIView show up ok when button is clicked. The issue is dropdown UIView's location that is always the same origin X=120, Y=0.
This is how I try to do it:
I have Vertical Stack with 4 rows
In 4th row I have label(width=120 almost same as X coordinate above) and a button that triggers UIView to show
I am using button to show dropdown list(basically UIView) that should appear just below button when the button is tapped, but it always appears at origin x=120 Y=0 , basically pinned to top of the right column in Vertical Stack View. Vertical Stack View has 1st column with labels, and second column with different controls like buttons etc.
func addTransparentView(frames: CGRect)
{
let window = UIApplication.shared.windows.filter {$0.isKeyWindow}.first
transparentView.frame = window?.frame ?? self.view.frame
//some of the stuff I tried to at least centre dropdownUIView
//transparentView.center.x = window?.center.x ?? self.view.center.x
//transparentView.center.y = window?.center.y ?? self.view.center.y
self.view.addSubview(transparentView)
tvPriority.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.width, width: frames.width, height: 0)
//some of the stuff I tried to at least centre UIView
//tvPriority.center = verticalStackView.convert(verticalStackView.center, from:tvPriority)
//tvPriority.center.x = view.center.x
//tvPriority.center.y = view.center.y
self.view.addSubview(tvPriority)
tvPriority.layer.cornerRadius = 5
transparentView.backgroundColor = UIColor.black.withAlphaComponent(0.9)
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(removeTransparentView))
transparentView.addGestureRecognizer(tapGesture)
transparentView.alpha = 0
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 1.0, initialSpringVelocity: 1.0, options: UIView.AnimationOptions.curveEaseInOut, animations: {self.transparentView.alpha = 0.5
self.tvPriority.frame = CGRect(x: frames.origin.x, y: frames.origin.y + frames.height, width: frames.width, height: 193)
}, completion: nil)
}
To successfully make dropdown list I need UIView to show up just below buttons frame(X, Y, width, height). But although button is in the 4th row which should be position with much higher Y value, buttons frame is always at X=120, Y=0, so my UIView is always pinned to this location way above button that is supposed to simulate dropdown.
Questions
1. What am I missing with positioning of the dropdown UIView? Why is buttons position Y=0 when the button is in 4th row of Vertical Stack View, with obviously much higher Y position? I also tried to simply centre this dropdown in the centre of screen but that also does not work.
2. I transitioned to iOS development from the world of web development, and I used dropdown a lot in my career. Should I just use Picker View instead? Or alert? What is the most common and most standard way of offering list of mutually exclusive options to user in Swift app?
Thanks a lot
Your button is a subview of the stackView, so its frame is relative to the frame of the stackView.
To get its frame (rect) in the view's coordinate space, you'll want to use .convert. Assign this action to one of your buttons in the stackView:
EDIT Fixed the code example... I had not checked it before posting.
class ConvertViewController: UIViewController {
#IBOutlet var dropDownView: UIView!
#IBAction func didTap(_ sender: Any) {
guard let btn = sender as? UIButton else {
fatalError("Sender is not a button!")
}
guard let sv = btn.superview as? UIStackView else {
fatalError("Sender is not in a stackView!")
}
let btnRect = btn.frame
let cvtRect = sv.convert(btn.frame, to: view)
print("button frame in stackView:", btnRect)
print("button frame in main view:", cvtRect)
let dropDownRect = dropDownView.bounds
let cvtCenterX = cvtRect.origin.x + (cvtRect.size.width / 2.0)
let viewX = cvtCenterX - (dropDownRect.width / 2.0)
let newOrigin = CGPoint(x: viewX, y: cvtRect.minY + cvtRect.height)
dropDownView.frame.origin = newOrigin
}
}
If you look at the output in the debug console, you should see something like this:
button frame in stackView: (0.0, 114.0, 46.0, 30.0)
button frame in main view: (164.5, 489.5, 46.0, 30.0)
As you can see, the rect (frame) of my 4th button in my stackView has an origin of 0.0, 114.0, but after converting it to my view coordinate space, the rect's origin is 164.5, 489.5.
You can now position your "dropdown list" relative to the converted rect.
As a side note, you may want to look at UIPopoverPresentationController.

Navigating Text box on a pdf is not working any more after iOS 13 - seems gestures issue using PDFKIT

I implemented a project I used PDFKIT to do some how adding a floating Textbox on a pdf and it can navigate by users touch on screen using GestureRecognizer to move and put where users decide to. it has worked perfectly before iOS 13, but after it, it does not give you the chance to move the text box and I tried many ways to solve it but all failed.
It seems that the problem somehow relates to GestureRecognizer but I am not sure, and also I really do not know how to fix it. I was working on this project on github https://github.com/kishikawakatsumi/BookReader and tried to expand it some how.
and here is my codes project, I am inserting the whole functions, I know it seems a little complicated. Really sorry about this. But if you think it might be from gesture recogniser, just look at the codes belong to these functions.
func getPDFFrame(todo: String)
{
let pdfSubVws = pdfView.subviews
print("subVws ",pdfSubVws)
for subVws in pdfSubVws
{
if (String(describing: subVws).range(of:"UIPageViewControllerContentView") != nil)
{
let pageVc = subVws.subviews
for subVws in pageVc
{
if (String(describing: subVws).range(of:"UIQueuingScrollView") != nil)
{
let QueueVc = subVws.subviews
// print("QueueVcQueueVc ", QueueVc)
for subVws in QueueVc
{
if (String(describing: subVws).range(of:"UIView") != nil)
{
let viewVc = subVws.subviews
print("\n\nviewVcVcQueueVc ", viewVc) //viewVcVcQueueVc []
for subVws in viewVc
{
if (String(describing: subVws).range(of:"UIView") != nil)
{
let SubviewVc = subVws.subviews
// print("\n\nSubviewVcSubviewVc ", SubviewVc)
for subVws in SubviewVc
{
if (String(describing: subVws).range(of:"UIScrollView") != nil)
{
let finalVw = subVws.subviews
// print("\n\nfinalVw ", finalVw)
//
for subVws in finalVw
{
if (String(describing: subVws).range(of:"UIView") != nil)
{
// print("subVwssubVws ", subVws)
let pagView = subVws.subviews
// pdfLandingView =
for subVws in pagView
{
// print("\n\n EndVwssubVws ", subVws)
if (String(describing: subVws).range(of:"PDFPageView") != nil)
{
let pageLandVw = subVws.subviews
print("\n\n WasVwssubVws ", pageLandVw)
// hideBars()
// Myedit()
for subVws in pageLandVw
{
if (String(describing: subVws).range(of:"UITextView") != nil)
{
print("dddddddd")
let txtVw = subVws as! UITextView
if todo == "firstResponder"
{
let rect = pdfView.currentPage?.bounds(for: .mediaBox)
usingHeightText = txtVw.frame.size.height
usingWidthText = txtVw.frame.size.width
let xPoint = txtVw.frame.origin.x
let yPoint = (rect?.height)! - (txtVw.frame.origin.y) - txtVw.frame.size.height
lastTextAnnoPoint = CGPoint(x: xPoint, y: yPoint)
findHavingTextAnnotation(annoPoint: CGPoint(x: xPoint, y: yPoint))
// txtVw.contentInset = UIEdgeInsetsMake(0, 10, 0, 0)
print("C_Sz ", txtVw.contentSize)
print("C_Off ", txtVw.contentOffset)
let widthMoveView = UIView(frame: CGRect(x: txtVw.frame.size.width - 20, y: 0, width: 20, height: txtVw.frame.size.height))
widthMoveView.backgroundColor = UIColor.brown.withAlphaComponent(0.0)
widthMoveView.tag = 333
let wdImgVw = UIImageView()
wdImgVw.frame.origin.x = 0
wdImgVw.frame.origin.y = 8
wdImgVw.frame.size.width = 20 // widthMoveView.frame.width
wdImgVw.frame.size.height = 20 // widthMoveView.frame.height
wdImgVw.contentMode = .scaleAspectFit
wdImgVw.image = UIImage(named: "widthLeftRight")
wdImgVw.layer.cornerRadius = 10
// wdImgVw.backgroundColor = UIColor.orange
widthMoveView.addSubview(wdImgVw)
txtVw.addSubview(widthMoveView)
//
let tapVw = UIPanGestureRecognizer(target: self, action: #selector(self.handleWidthTapVw(_:)))
widthMoveView.addGestureRecognizer(tapVw)
}
else
//....and here I am going to omit the other parts of my codes
and here I am going to omit the other parts of my codes because seems that the problem occurs in this line of code
for subVws in finalVw
and here other parts of my codes not sure you may need it or not
let linkAnn = PDFAnnotation.init(bounds: CGRect(x: 150, y: 450, width: 180, height: usingHeightText), forType: PDFAnnotationSubtype.widget, withProperties: nil)
linkAnn.widgetFieldType = .text
linkAnn.isMultiline = true
linkAnn.contents = "fileNumber\(pdfClickedRow)PageNumber\(getPagenumber)addtext\(AddTextCount)" //"\(AddTextCount)"
linkAnn.widgetStringValue = NSLocalizedString("placeholder", comment: "")
linkAnn.font = UIFont(name: usingFontName, size: CGFloat(usingFontSize))
linkAnn.fontColor = usingFontColor
linkAnn.backgroundColor = usingBGFontColor.withAlphaComponent(usingBGFontAlpha)
linkAnn.setValue("addtext\(AddTextCount)", forAnnotationKey: PDFAnnotationKey.name)
// print("linkAnn.cont ",linkAnn.contents)
let index = pdfDocument?.index(for: pdfView.currentPage!)
pdfView.document?.page(at: index!)?.addAnnotation(linkAnn)
var createdColorDict = [String : Any]()
createdColorDict["BGColor"] = usingBGFontColor
createdColorDict["BGOpacity"] = usingBGFontAlpha
textAnnoStoredDict["fileNumber\(pdfClickedRow)PageNumber\(getPagenumber)addtext\(AddTextCount)"] = createdColorDict
// pdfView.currentPage?.addAnnotation(linkAnn)
bottomColorView.isHidden = true
bottomColorView.backgroundColor = UIColor(red: 0/255, green: 0/255, blue: 0/255, alpha: 1.0)
sideWidthHeightVw.backgroundColor = UIColor(red: 240/255, green: 240/255, blue: 240/255, alpha: 1.0)
sideWidthHeightVw.isHidden = true
barHideOnTapGestureRecognizer.isEnabled = false
hideBars()
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow),
name: NSNotification.Name.UIKeyboardWillShow, object: nil)
//keyboardWillShow
Its hard for me to understand what could be the problem by looking at your code. Here's my high level suggestion:
1. Make a backup of your code first to make sure you can come back to your stable state.
2. Start cutting the code in half - eliminating half of the logic path each time. For example: Instead of using a PDF view, use a regular view with a text box (to find out if the problem is with the PDF view). Instead of using a UITextField, use just a UIView to see if it's a problem with UITextField.
Basically remove all the complexity gradually until you are left with a simple piece of code that allows you to move the text field.
Also: avoid having such long methods. I try to keep methods down to 30 lines max, and 2 levels of nesting max. Otherwise you will have more logic paths than you can maintain or understand...
You are also relying on strings for object types. This is fragile. Maybe there's another way to find the PDF View instead of crawling the whole view hierarchy for it, and checking strings? (They are not compiler checked, so any change in the future or change between OS / Third party versions will break your code).
If your text field doesn't move at all, try to remove all that math you're doing with the frame, and just get it to move using gesture recognizer to any coordinate (for example: 200, 200), just to see that something is holding the text field in place (like a constraint). Do you have constraints that can be holding it? Use the "View Inspector" to check and verify how the view is embedded and what's above / below it. Is there anything that could be intercepting touches to your gesture recognizer? Is it receiving touches and firing it's event? You need to describe more exactly what the problem is... Please refer to my answer here for things to check on the Gesture recognizer itself. UIPanGestureRecognizer is not working in iOS 13
Your view hierarchy is so complex with this PDF view that it's hard to say what could be happening with other Touch events inside it . (unless you understand exactly how the third party PDF View works). I would try to isolate the problem, whether it's related to the PDF view or not. Get it working in just a regular View first. Then work on the PDF view by using hitTest, and put a print statement there, to see who is competing for the HIT test.

how to fix view under navigation bar swift

i make popup using EzPopup library, i get problem when i put popup under navigation bar.
this my code
#IBAction func showTopRightButton(_ sender: Any){
guard let pickerVC = pickerVC else { return }
pickerVC.delegate = self
let popupVC = PopupViewController(contentController: pickerVC, position: .topRight(CGPoint(x: 0, y: navigationController!.navigationBar.frame.height+20)), popupWidth: 100, popupHeight: 200)
popupVC.cornerRadius = 5
present(popupVC, animated: true, completion: nil)
}
i got a problem like my image
enter image description here
how to make same in iphone x and iphone x
In iPhone X portrait mode, the status bar is taller — 44 pts, not 20 pts
You need to add the statusBar Frame height also. Try below code:
//1.0 Get the Top bar height
let topBarHeight = UIApplication.shared.statusBarFrame.size.height + (self.navigationController?.navigationBar.frame.height ?? 0.0)
let popupVC = PopupViewController(contentController: pickerVC, position: .topRight(CGPoint(x: 0, y: topBarHeight)), popupWidth: 100, popupHeight: 200)
You can read more information on UILayout here.

How to capture image of entire view's contents when bigger than screen

I need my app to render everything that a view controller has the potential to display (including off-screen content) except for the top and bottom navigation bars.
The first image, below, shows the view controller at runtime. The action menu triggers the following code which is adapted the code sample from the answer given here :
#IBAction func actionMenu(_ sender: Any) {
let activityItems = [generateImageOfTableView(tblview: tourneyEntrants)]
let activityController = UIActivityViewController(activityItems: activityItems, applicationActivities: nil)
activityController.popoverPresentationController?.sourceView = self.view
activityController.popoverPresentationController?.sourceRect = self.view.frame
self.present(activityController, animated: true, completion: nil)
}
func generateImageOfTableView(tblview: UITableView) -> UIImage {
var image = UIImage()
UIGraphicsBeginImageContextWithOptions(tblview.contentSize, false, UIScreen.main.scale)
// save initial values
let savedContentOffset = tblview.contentOffset;
let savedFrame = tblview.frame;
let savedBackgroundColor = tblview.backgroundColor
// reset offset to top left point
tblview.contentOffset = CGPoint(x: 0, y: 0);
// set frame to content size
tblview.frame = CGRect(x: 0, y: 0, width: tblview.contentSize.width, height: tblview.contentSize.height);
// remove background
tblview.backgroundColor = UIColor.clear
// make temp view with scroll view content size
// a workaround for issue when image on ipad was drawn incorrectly
let tempView = UIView(frame: CGRect(x: 0, y: 0, width: tblview.contentSize.width, height: tblview.contentSize.height))
// save superview
let tempSuperView = tblview.superview
// remove scrollView from old superview
tblview.removeFromSuperview()
// and add to tempView
tempView.addSubview(tblview)
// render view
// drawViewHierarchyInRect not working correctly
tempView.layer.render(in: UIGraphicsGetCurrentContext()!)
// and get image
image = UIGraphicsGetImageFromCurrentImageContext()!
// and return everything back
tempView.subviews[0].removeFromSuperview()
tempSuperView?.addSubview(tblview)
// restore saved settings
tblview.contentOffset = savedContentOffset;
tblview.frame = savedFrame;
tblview.backgroundColor = savedBackgroundColor
UIGraphicsEndImageContext()
return image
}
The second image, below, shows the image captured from this code.
There are two problems with it.
The first is that it is ignoring the text field and label above the table. I know that the code doesn't look for this, so I am looking for some guidance on how to capture the superview's contents (minus the navigation bars).
Second, the table view contains 18 columns of numbers but these aren't captured. So, the code copes with the height of the table being beyond the screen but not with the width. I've looked at whether auto layout maybe causing this, but cannot see anything obvious.