IphoneX simulator closing popovers when I interact with them - swift

I'm having a problem with the iPhone X simulator in Xcode behaving differently to other simulators.
What I'm trying to do it click on a view which brings up a UIPicker. When a date is selected and the user clicks ok the popover disappears and a label in the view is updated with the string of what was selected. This is working on the 2 physical devices I'm using (iPhone6 plus and iPad). It's also working in simulators for iPhone 5 and 8. However when I try the iPhoneX it doesn't work.
As soon as the user the taps the picker or moves it it fades of the screen and nothing gets updated. As I don't have a physical iPhoneX to test with I don't know if this is just a simulator issue or if it will also happen on an actual device. Is this just a simulator issue? How can I fix this? Will this also happen on an actual device?
Below is the function I'm using to display the custom popover containing the picker view.
/// When the view is tapped it will display user options and then update the label in the view.
///
/// - Parameters:
/// - box: LabelAndWhiteBarView is my own custom UIView for this project.
/// - strings: array of strings to be displayed by the picker
func displayStringPicker(box: LabelAndWhiteBarView, strings: [String]){
// Custom viewcontroller containing a UIPickerView, title UILabel and a cancel and OK UIButton.
let floatingPickerViewController:FloatingPickerViewController = UIStoryboard(name: "FloatingPickerStoryboard", bundle: nil).instantiateInitialViewController()! as! FloatingPickerViewController
floatingPickerViewController.modalPresentationStyle = .popover
floatingPickerViewController.preferredContentSize = defaultPickerSize // set elsewhere so it doesnt cover the whole screen
//Adds a blank string in front of each array so the user can always pick and empty option
var stringsWithBlankFirstOption = strings
stringsWithBlankFirstOption.insert("", at: 0)
floatingPickerViewController.stringArray = stringsWithBlankFirstOption
//tells the picker view which view to update
floatingPickerViewController.senderBox = box
floatingPickerViewController.delegate = self
floatingPickerViewController.view.layer.borderWidth = 1
floatingPickerViewController.view.layer.borderColor = UIColor.clear.cgColor
let popoverMenuViewController = floatingPickerViewController.popoverPresentationController
popoverMenuViewController?.permittedArrowDirections = UIPopoverArrowDirection(rawValue:0) // removed the arrow thats defauls on a popover
popoverMenuViewController?.delegate = self
popoverMenuViewController?.sourceView = self.view
popoverMenuViewController?.sourceRect = CGRect(
x: 0,
y: 0,
width: self.view.frame.width,
height: self.view.frame.height)
floatingPickerViewController.dismissBlock = {
self.dismiss(animated: false, completion: nil)
}
present(
floatingPickerViewController,
animated: false,
completion: nil)
/// set the picker to preselect what was already selected
let indexOfSelectedItem = floatingPickerViewController.stringArray.index(of: box.displayLabel.text ?? "") ?? 0
//pre selects the last selected string
floatingPickerViewController.numberPicker.selectRow(indexOfSelectedItem, inComponent: 0, animated: false)
}

Related

UIButton Not Clickable when added UITabbar

When added UIButton on UITabbar to middle as shown in figure.
The button action on above the UITabBar unable to click
func setupMiddleButton() {
plusButton = UIButton(frame: CGRect(x: 0, y: 0, width: 64, height: 64))
var menuButtonFrame = plusButton.frame
menuButtonFrame.origin.x = tabBar.bounds.width/2 - menuButtonFrame.size.width/2
let hasNotched :Bool? = UIDevice.current.hasNotch
if hasNotched != nil {
menuButtonFrame.origin.y = tabBar.bounds.height - menuButtonFrame.height - 15
} else {
menuButtonFrame.origin.y = tabBar.bounds.height - menuButtonFrame.height - 50
}
plusButton.frame = menuButtonFrame
plusButton.setTitle("+", for: .normal)
plusButton.titleLabel?.font = UIFont.helveticaNeue(ofSize: 40)
plusButton.backgroundColor = UIColor.init(hexString: "5E71FE")
plusButton.titleEdgeInsets = UIEdgeInsets(top: 0,left: 10,bottom: 10,right: 10)
tabBar.addSubview(plusButton)
plusButton.layer.cornerRadius = menuButtonFrame.height/2
plusButton.addTarget(self, action: #selector(plusButtonAction(sender:)), for: .touchUpInside)
}
You need to override the hitTest method in your custom tab bar class like this
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView?
{
guard !clipsToBounds && !isHidden && alpha > 0 else { return nil }
for member in subviews.reversed()
{
let subPoint = member.convert(point, from: self)
guard let result = member.hitTest(subPoint, with: event)
else { continue }
return result
}
return nil
}
Basically the problem is that upper part is not clickable because it is outside of the bounds of main content view of tab bar.
This method will check if the tap is inside the bounds of the view, if it is it will return the view and the action for that button will get called.
Documentation by apple: Link
P.s I was facing the same issue recently and got this help which worked smooth.
I suspect that what you are trying to do is not possible, or at the least, not supported by Apple. (And thus not recommended since you might find a way to make it might work today but not in some future OS version.)
As a rule, Apple does not support you adding custom view objects to system components like tab bars, navigation bars, stack views, table/collection view controllers, etc except through a documented API.
I would suggest NOT doing what you are trying to do. instead, add a button in the content view of the tab bar controller. I don't know if you'll be able to make it partly cover the tab bar like you are trying to do however.
Add the button to the view of the UITabbarController instead of adding to the TabBar. And then reposition the button, it will work.

Snapshot of screen shows toolbar on device but not on simulator

I have a toolbar that's instantiated on viewDidLoad on top of a webkit. When I take a snapshot on the simulator, the toolbar is missing which is what I would like. When built on the device, the toolbar is there.
I tried to hide the toolbar with:
toolbar.isHidden = true
but the application crashes with toolbar being nil. If I change it to:
toolbar?.isHidden = true
It still shows up considering it still thinks it's nil.
The toolbar is set up on viewDidLoad by calling another function:
var toolbar : UIToolbar!
override func viewDidLoad() {
super.viewDidLoad()
setUpToolBar()
}
func setUpToolBar() {
let saveButton = UIBarButtonItem(barButtonSystemItem: .save, target: self, action: #selector(takeScreenshot))
...
let toolbar = UIToolbar(frame: CGRect(x: 0, y: 300, width: 200, height: 50))
toolbar.setItems([saveButton,flexibleSpaceFillerLeft,userAgentButton,flexibleSpaceFillerRight,doneButton], animated: true)
view.addSubview(toolbar)
}
The code for my snapshot is below. This is where I tried to hide the toolbar before taking the snapshot.
#objc func takeScreenshot() {
webView.takeSnapshot(with: nil, completionHandler: { (image,error) in
if let image = image {
self.screenshotOfWindow = image
self.showScreenshotEffect()
self.saveAllData()
} else {
print (error?.localizedDescription as Any)
}
})
}
Here's the screen I need to take a screenshot of:
The red box in the screenshot is the bar that I need to disappear from the screenshot.
I'd like to be able to take the screenshot without the bottom bar in view. As stated before, this works in the simulator, but the device always shows the bar. There's also a "navigation controller" gap at the top of the screenshot since the top bar covers part of the screen at top, but this is just blank and something I can address later.
I just wanted to come back and answer how I solved this. The webview is embedded in a navigation controller, but I was creating the toolbar programmatically on viewDidLoad by calling a setupToolBar function I had created early on in the project. I could hide the toolbar, but it was still being captured while taking the screenshot. I commented all of that code out and used the navigation controller's toolbar instead. Now when I take the screenshot, the bottom and top bar of the navigation controller is not part of the screenshot.

How do i get rid of xib after presenting it?

I am creating my own UIImagePicker Overlay and am using a Xib in order to display the overlay. Whenever I present the imagepicker, it seems to present twice, once when I call the main bundle to load the nib and the other when I explicitly present the imagepickercontroller. The main problem i'm having is when I dismiss the imagepickercontroller, the first one goes, which is great, but the underlying view of the xib still remains and doesn't go regardless of what i do.
I tried to convert the Objective-C example project for how to create a custom uiimagepickercontroller Apple has on their website into Swift. I'm not sure what I"m doing wrong, here is my code:
func showImagePickerForSourceType(sourceType: UIImagePickerControllerSourceType) {
let picker = UIImagePickerController()
picker.modalPresentationStyle = UIModalPresentationStyle.CurrentContext
picker.sourceType = sourceType
picker.delegate = self
if sourceType == UIImagePickerControllerSourceType.Camera {
//gets rid of the out of the box imagepicker
picker.showsCameraControls = false
//loads the overlay xib for the camera
NSBundle.mainBundle().loadNibNamed("OverlayView", owner: self, options: nil)
self.overlayView.frame = (picker.cameraOverlayView?.frame)!
picker.cameraOverlayView = self.overlayView
self.overlayView = nil
//makes the image capture screen the full screen size
let screenSize: CGSize = UIScreen.mainScreen().bounds.size
let cameraAspectRatio: Float = 4.0 / 3.0
let imageWidth:Float = floorf(Float(screenSize.width) * cameraAspectRatio)
let scale: Float = ceilf((Float(screenSize.height + 120) / imageWidth) * 10.0) / 10.0
picker.cameraViewTransform = CGAffineTransformMakeScale(CGFloat(scale), CGFloat(scale))
}
self.imagePickerController = picker
self.presentViewController(self.imagePickerController, animated: true, completion: nil)
}
Here is the code for a button on the xib dismissing the view. How can i fix this? Because i'm printing out the array of view controllers on the navigation stack, and it only shows one.
#IBAction func goBack(sender: AnyObject) {
self.imagePickerController.dismissViewControllerAnimated(true, completion: nil)
}
It sounds like you have your view controller's view outlet connected to the view in your .xib file in addition to the overlayView outlet. This would probably cause what you're describing.

Howto Size the Modal View programmatically (SWIFT)

I simply want to present a small option dialog over an existing main UIViewController/UIView , so that on an IPad I would see a small Dialog and in the Background I will see the Main View.
I managed to show a UIViewController/UIView in a modal view style as follow:
func showoptions(){
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewControllerWithIdentifier("Options") as! UIViewController
controller.modalPresentationStyle = UIModalPresentationStyle.Popover
let popoverPresentationController = controller.popoverPresentationController
// result is an optional (but should not be nil if modalPresentationStyle is popover)
if let _popoverPresentationController = popoverPresentationController {
// set the view from which to pop up
_popoverPresentationController.sourceView = self.view;
//_popoverPresentationController.sourceRect = CGRectMake(60, 100, 500, 500)
//_popoverPresentationController. .setPopoverContentSize(CGSizeMake(550, 600), animated: true)
//_popoverPresentationController.sourceView.sizeToFit();
// present (id iPhone it is a modal automatic full screen)
self.presentViewController(controller, animated: true, completion: nil)
}
}
But I have still some issues:
1. Howto get rid of the arrow shown at the border.
2. Howto size this modal view. It is shown to small and I would like to fit it to the largest controls in the UIControllerView/UIView.
any help ?
Try changing _popoverPresentationController.permittedArrowDirections to empty option set
Change controller.preferredContentSize to match your desired size
I needed something similar and ended up presenting a view controller as a modal with a transparent background over the current context. There I made a smaller opaque UIView with what I wanted.

Displaying UIDatePicker as Popover within UITableView on iPhone

I would like display a date picker with a UIModalPresentationPopover presentation style, and while it's working fine on an iPad, it results in a black screen when displayed on an iPhone. Edit: Specifically, it is simply presented full screen, so the effect is a black screen unless I add colours to elements manually. The default colours work fine for Popover presentation.
The code generating the picker and the popover is:
let datePicker = UIDatePicker()
datePicker.datePickerMode = .DateAndTime
datePicker.minuteInterval = 5
let vc = UIViewController()
vc.view.addSubview(datePicker!)
vc.preferredContentSize = datePicker.frame.size
vc.modalPresentationStyle = .Popover
let cell = self.tableView.cellForRowAtIndexPath(NSIndexPath(forRow: 0, inSection: 1))
let ppc = vc.popoverPresentationController
ppc?.delegate = self
ppc?.sourceRect = cell.accessoryView!.frame
ppc?.sourceView = cell
self.presentViewController(vc, animated: true, completion: nil)
The value of cell appears correct on both types of devices, but I notice that the size of the view of the presented viewController gets messed up on the iPhone. After presentation is finished, the value of preferredContentSize property is (320.0, 216.0) on both devices, but view.frame.size becomes (320.0, 568.0) on the iPhone (which is full screen on iPhone 5, and it acts similarly on iPhone as well), while on iPad it still matches the preferred size.
Looks great on iPad:
I've had no trouble presenting the same kind of UI from a UIViewController that was not UITableViewController. Is there anything else that needs to be done to allow this style of presentation with a tableView on an iPhone?
Edit
Fixed. To allow this, need to add a method to the presenting controller:
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
This is what I found on the docs from UIViewController
modalPresentationStyle
The presentation style determines how a modally
presented view controller is displayed onscreen. In a horizontally
compact environment, modal view controllers are always presented
full-screen
What you can do is, on the iPhone, set the backgroundColor of the view to black with `.15 alpha, and position the picker at the bottom.
You might need to set modalPresentationStyle to UIModalPresentationOverFullScreen, because otherwise it removes the views underneath.