Change height of UIPopoverController, to height of UITextView - ios

In my app there is the need for a popover element, that displays the full description.
There is a normal UITextView in the UIViewController that displays the first 4 lines of the description. Now when the user clicks on that description the full description must be displayed. This will be done with the popover control recently introduced for the iPhone.
At the moment everything works, except for the fact that I want the popover to have a height that is exactly the height of the string in the UITextView in the DescriptionPopupViewController. This way if the description is longer, it will automatically make the popover higher.
The following code is called when the user clicks the UITextView:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
if textView == descriptionTextView {
let sb = UIStoryboard(name: "Main-alternative", bundle: nil)
let popoverController = sb.instantiateViewControllerWithIdentifier("descriptionControllerID") as! DescriptionPopupViewController
popoverController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverController.popoverPresentationController?.delegate = popoverController;
popoverController.popoverPresentationController?.sourceView = descriptionTextView
popoverController.popoverPresentationController?.sourceRect = descriptionTextView.frame
popoverController.descriptionText = currentObject?["description"] as! String
if let frameHeight = popoverController.textView?.frame.height {
popoverController.preferredContentSize = CGSizeMake(fixedWidth, frameHeight)
}
self.presentViewController(popoverController, animated: true, completion: nil)
}
return false
}
So the preferredContentSize should get the height of the enclosed UITextView in the DescriptionPopupViewController. However when I try to do something like this it won't work:
I think because the frameHeight is nil

When the UIViewController is created its view hierarchy may not (and so all subviews, properties will still be nil), for optimization reasons it may not be loaded. You need to the access controller's view to force it to load its view hierarchy. So you need to update you code like the following:
func textViewShouldBeginEditing(textView: UITextView) -> Bool {
if textView == descriptionTextView {
let sb = UIStoryboard(name: "Main-alternative", bundle: nil)
let popoverController = sb.instantiateViewControllerWithIdentifier("descriptionControllerID") as! DescriptionPopupViewController
popoverController.modalPresentationStyle = UIModalPresentationStyle.Popover
popoverController.popoverPresentationController?.delegate = popoverController;
popoverController.popoverPresentationController?.sourceView = descriptionTextView
popoverController.popoverPresentationController?.sourceRect = descriptionTextView.frame
// forces to load its view hierarchy
var view = popoverController.view
popoverController.descriptionText = currentObject?["description"] as! String
if let frameHeight = popoverController.textView?.frame.height {
popoverController.preferredContentSize = CGSizeMake(fixedWidth, frameHeight)
}
self.presentViewController(popoverController, animated: true, completion: nil)
}
return false
}
I hope this help you.

Related

How to find and replace a subview from UIScrollView

I have a scrollview to which I have added 4 subviews at ViewDidLoad, now during app usage on an event I want to replace one of the subview with a new view.
I am trying to add tag to subview and find it and replace but the tag seems to be not found. Also not able to find a correct way to replace a view
let subViews = self.scrollView.subviews
for subview in subViews{
if subview.tag == 1000 {
let tempView = subview
tempView = newView
}
}
I am sure above code isn't right , here code never enters inside the 'if' and when i see all the subviews in debug area i dont see the tag as well.
code from ViewDidLoad
let storyboard = UIStoryboard(name: "Main", bundle: nil)
self.overLeftVc = storyboard.instantiateViewController(withIdentifier: "settingViewController")
self.leftVc = storyboard.instantiateViewController(withIdentifier: "giftCardListViewController")
self.middleVc = storyboard.instantiateViewController(withIdentifier: "redeemViewController")
self.rightVc = storyboard.instantiateViewController(withIdentifier: "homeViewController")
self.overRightVc = storyboard.instantiateViewController(withIdentifier: "ticketListViewController")
let isShowBotView = self.config.getValue("isSystemGenerated", fromStore: "settings") as! Bool
if isShowBotView == true {
self.nextOfOverRightVc = storyboard.instantiateViewController(withIdentifier: "chatViewController")
}
else {
self.nextOfOverRightVc = storyboard.instantiateViewController(withIdentifier: "tileMainViewController")
}
self.nextOfOverRightVc.view.tag = 1000
addChildViewController(overLeftVc)
addChildViewController(leftVc)
addChildViewController(middleVertScrollVc)
addChildViewController(nextOfOverRightVc)
// addChildViewController(tileMainRightVc)
scrollView.addSubview(overLeftVc.view)
scrollView.addSubview(leftVc.view)
scrollView.addSubview(middleVertScrollVc.view)
scrollView.addSubview(nextOfOverRightVc.view)
save the subview's frame in a variable , remove that subview and insert a new subview at that Frame
for subview in subViews{
if subview.tag == 1000 {
self.frameOfViewToBeReplaced = subview.frame
subview.removeFromSuperView()
}
}
let newView = UIView(frame : self.frameOfViewToBeReplaced)

Drop down list ios swift

I want to have a small UItableView that popup when clicked and shows some numbers in the list.
I tried to use popoverPresentationController but it appears full screen for iOS(iPhone) devices.
below is the code for same -
let filterVC = TableViewController(nibName: "TableViewController", bundle: nil)
filterVC.preferredContentSize = CGSize(width: 300, height: 200)
filterVC.modalPresentationStyle = UIModalPresentationStyle.popover
present(filterVC, animated: true, completion: nil)
let popoverPresentationController = filterVC.popoverPresentationController
if let pop = filterVC.popoverPresentationController {
pop.delegate = self
}
popoverPresentationController?.sourceView = sender as? UIView
popoverPresentationController?.sourceRect = sender.frame
//-------
with below method also
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// Return no adaptive presentation style, use default presentation behaviour
return .none
}
//-----
Any hint in right direction would be highly appreciated.
working sample would be greatly helpful
What I am trying to achieve as below
UPDATE
There is a useful library you may want to give a try.
It's because your pop.delegate was assigned after you present the filterVC.
Move this
if let pop = filterVC.popoverPresentationController {
pop.delegate = self
pop.sourceView = sender
pop.sourceRect = sender.bounds
}
present(filterVC, animated: true, completion: nil)
to the init of your filterVC should do the trick. Btw, I didn't see anywhere you have assigned sourceView and sourceRect for your popoverPresentationController. Moving pop.delegate = self to this part should be appropriate. Something like
init(for sender: UIView)) {
super.init(nibName: nil, bundle: nil)
modalPresentationStyle = .popover
guard let pop = popoverPresentationController else { return }
pop.sourceView = sender
pop.sourceRect = sender.bounds
pop.delegate = self
}

How to display custom size (using preferredContentSize) popup from a "custom" table cell (UITableViewCell)

Summary:
I am able to get a full-screen popover but NOT a custom-size popover through a button action within a custom table cell. I am able to get it working though when the button is not within a custom cell. Upon debugging it seems that when the button is within the custom table cell the delegate object of the popoverPresentationController is not being called which is the one that resizes the popover window.
Details:
Following is the code that works when button is outside of UITableViewCell:
import UIKit
import AVKit
import CoreGraphics
class ViewController: UIViewController, UIPopoverPresentationControllerDelegate {
func adaptivePresentationStyle(for controller: UIPresentationController) -> UIModalPresentationStyle {
// This delegate function ensures that popup size is constrained to
// what's specified in preferredContentSize earlier.
return UIModalPresentationStyle.none
}
// This is triggered by a button in UIViewController.. nothing to do
// with the UITable here. This implementation works.
#IBAction func buttonpressed(_ sender: AnyObject) {
let sb = self.storyboard
let vc: AnyObject! = sb!.instantiateViewController(withIdentifier: "popover")
let pc = vc as! PopOverWindow
pc.modalPresentationStyle = UIModalPresentationStyle.popover
pc.preferredContentSize = CGSize(width: 100, height: 100)
// Needs to be called BEFORE we access
// popoverPresentationController below. Counter-intuitive but this
// is how apple docs recommend it
self.present(pc, animated: true, completion: nil)
let popover = pc.popoverPresentationController
popover?.permittedArrowDirections = UIPopoverArrowDirection.any
popover?.delegate = self
let button = sender as! UIButton
// Need both statements below so that popover knows where to be
// tethered
popover?.sourceView = sender as? UIView
// the position of the popover where it's showed
// do not use buttons.frame
popover?.sourceRect = button.bounds
}
Important function here is adaptivePresentationStyle() which makes sure that the popup is not full screen.
Now, let's try to the same in a custom UITableViewCell. Biggest change here is that UITableViewCell is NOT a descendant of UIViewController
#IBAction func infoButtonTouchDown(sender: AnyObject) {
let sb = UIStoryboard(name: "Main", bundle: NSBundle.mainBundle())
let vc: AnyObject! = sb.instantiateViewControllerWithIdentifier("popover")
let pc = vc as! PopOverWindow
pc.modalPresentationStyle = .Popover
pc.preferredContentSize = CGSizeMake(50,100)
// Custom View Cell is not UIViewController so I need to following
// to be able to call presentViewController later
// I could have also done
//self.window!.rootViewController?.presentViewController(..)
// but the results were the same
let t = self.window?.rootViewController as! UITabBarController
let n = t.selectedViewController as! UINavigationController
let m = n.visibleViewController as! MyTableViewController
// Important: this needs to be called BEFORE we access
// pc.popoverPresentationController object. Apple Docs say it.
m.presentViewController(pc, animated: true, completion: nil)
let popover = pc.popoverPresentationController
popover?.permittedArrowDirections = UIPopoverArrowDirection.Any
// I tried doing popover?.delegate = d to make the underlying table
// as the delegate object but the results were same
popover?.delegate = self
let viewForSource = sender as! UIView
popover?.sourceView = m.view
// the position of the popover where it's showed
// just some random position in view.
popover?.sourceRect = CGRectMake(100,100,1,1)
}
The TROUBLE in above code is that the delegate function adaptivePresentationStyle() is not being called and consequently I end up getting a full-screen popover and not a half-screen.
Any help is hugely appreciated!

How to display UIVideoEditorViewController full screen in iPad

I have used UIVideoEditorViewController for trimming selected video. The problem is that the editorController has to be presented in popover style in iPad. When I running it on iPad, the editor view on pop over the left corner instead of the full screen. Is there any way to make the popover view in full screen size? Thanks
if UIVideoEditorController.canEditVideoAtPath(tmp) {
editVideoViewController = self.storyboard?.instantiateViewControllerWithIdentifier("editorVC") as! EditorViewController
editVideoViewController.delegate = self
editVideoViewController.videoPath = tmp
editVideoViewController.videoMaximumDuration = 30
editVideoViewController.videoQuality = .TypeHigh
editVideoViewController.modalPresentationStyle = UIModalPresentationStyle.Popover
editVideoViewController.popoverPresentationController?.sourceView = editVideoViewController.view
self.presentViewController(editVideoViewController, animated: true, completion: nil)
}
There is no way to show UIVideoEditorController full screen. You can put it inside some container controller. And then configure this container controller preferredContentSize with screen bounds. You will get almost full-screen size popover.
let containerVC = UIViewController()
containerVC.preferredContentSize = UIScreen.main.bounds.size
containerVC.modalPresentationStyle = .popover
let ppc = containerVC.popoverPresentationController
ppc?.delegate = self
ppc?.sourceView = containerVC.view
ppc?.sourceRect = UIScreen.main.bounds
ppc?.permittedArrowDirections = .init(rawValue: 0 )
ppc?.canOverlapSourceViewRect = true
let videoController = UIVideoEditorController()
containerVC.addChild(videoController)
containerVC.view.addSubview(videoController.view)
videoController.didMove(toParent: containerVC)
self.present(containerVC, animated: true)

Real popover view programmatically on iPhone

EDIT:
Added approach 3 (see comments below).
I want to create little popovers in my app but I can't use the storyboard since the anchor points for those popovers will be UIControls that the user creates.
I want to create real popovers like those described here (not fullscreen ones). It works great using the storyboard but in this case i need it to work without segues.
Also I found this post where the person tries to do something very similiar. I tried to use the solution from that post as below in //approach 1. In //approach 2 I tried the solution on this site (even though it's for iPads only I think but I was out of ideas...).
// long press gesture: show additional control elements
func showLongPressMenu(recognizer: UILongPressGestureRecognizer) {
// approach 1
let newPopoverVC1 = UIViewController(nibName: "LinkAreaPopupView", bundle: NSBundle.mainBundle())
newPopoverVC1.modalPresentationStyle = .Popover
newPopoverVC1.preferredContentSize = CGSizeMake(137.0, 28.0)
var newPopoverController = newPopoverVC1.popoverPresentationController!
newPopoverController.delegate = self
newPopoverController.permittedArrowDirections = .Any
newPopoverController.sourceView = ???
newPopoverController.sourceRect = ???
presentViewController(newPopoverVC1, animated: true, completion: nil)
// approach 2
let newPopoverVC2 = UIViewController(nibName: "LinkAreaPopupView", bundle: NSBundle.mainBundle())
newPopoverVC2.modalPresentationStyle = .Popover
let popAnchorRect = self.frame
let newPopover = UIPopoverController(contentViewController: newPopoverVC2)
newPopover.presentPopoverFromRect(popAnchorRect, inView: ???, permittedArrowDirections: .Any, animated: true)
// approach 3
let vc = UIViewController()
vc.preferredContentSize = CGSizeMake(137.0, 28.0)
vc.modalPresentationStyle = .Popover
if let pres = vc.popoverPresentationController {
pres.delegate = self
}
// THIS DOES NOT WORK
self.superview.presentViewController(vc, animated: true, completion: nil)
let popView = LinkAreaPopupView()
vc.view.addSubview(popView)
popView.frame = vc.view.bounds
popView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
if let pop = vc.popoverPresentationController {
pop.sourceView = (self as UIView)
pop.sourceRect = (self as UIView).bounds
}
}
And the delegate function:
// don't allow to substitute the presentation style of popover controllers (to fullscreen for example)
func adaptivePresentationStyleForPresentationController(controller: UIPresentationController) -> UIModalPresentationStyle {
return .None
}
The "LinkAreaPopupView" is a XIB file containing the menu I want to show.
My problem is that the class that has this code is a sublass of UIControl and will be created by the user. It doesn't know the "presentViewController()" function. The "presentPopoverFromRect()" function is not allowed to be called on iPhones (throws an error saying something about iPads). And obviously I'm missing some arguments too (the "???" parts).
Hope I didn't forget anything important. Thanks in advance
After hours of trying I got it working. Together with this tutorial video which shows how to use views from XIB files and the comment from #matt I achieved to show popups from UIControls.
To instantiate my XIB file I created a UIView sublcass like this:
import UIKit
class LinkAreaPopupView: UIView {
// outlets
#IBOutlet var view: UIView!
#IBOutlet weak var btnDeleteArea: UIButton!
#IBOutlet weak var btnLinkTo: UIButton!
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
NSBundle.mainBundle().loadNibNamed("LinkAreaPopupView", owner: self, options: nil)
self.addSubview(self.view)
}
override init(frame: CGRect) {
super.init(frame: frame)
NSBundle.mainBundle().loadNibNamed("LinkAreaPopupView", owner: self, options: nil)
self.addSubview(self.view)
}
}
Then in my UIControl sublcass in the function that shall create the popups I added this:
// long press gesture: show additional control elements
func showLongPressMenu(recognizer: UILongPressGestureRecognizer) {
if recognizer.state == UIGestureRecognizerState.Began {
// approach 3 - https://github.com/mattneub/Programming-iOS-Book-Examples/blob/master/bk2ch09p477popoversOnPhone/PopoverOnPhone/ViewController.swift
let vc = UIViewController()
vc.preferredContentSize = CGSizeMake(137.0, 28.0)
vc.modalPresentationStyle = .Popover
if let pres = vc.popoverPresentationController {
pres.delegate = self
}
(self.delegate as! UIViewController).presentViewController(vc, animated: true, completion: nil)
let popView = LinkAreaPopupView()
vc.view.addSubview(popView)
popView.frame = vc.view.bounds
popView.autoresizingMask = .FlexibleWidth | .FlexibleHeight
if let pop = vc.popoverPresentationController {
pop.sourceView = (self as UIView)
pop.sourceRect = (self as UIView).bounds
}
}
}
The missing puzzle piece was to have the UIControl's delegate set to the UIViewController that lies underneath. And finally I had to cast (self.delegate as! UIViewController) because I implemented my own protocol to that delegate before and that's why I couldn't call .presentViewController(vc, animated: true, completion: nil) on it.
Voila.

Resources