PencilKit toolPicker is not showing up - ios

I am trying to create an app using PencilKit. I have the following code in one of my ViewControllers.
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
guard
let window = view.window,
let toolPicker = PKToolPicker.shared(for: window) else { return }
toolPicker.setVisible(true, forFirstResponder: canvasView)
toolPicker.addObserver(canvasView)
canvasView.becomeFirstResponder()
}
Although I am calling the setVisible function and making the canvasView the firstResponder, my toolPicker is not showing up, and printing toolPicker.isVisible is false.

Move your code to viewWillAppear(), this did it for me.
Apple is also doing it. I recommend to download and play with the Sample Code provided by Apple.

Related

Swift PDFKit: Inconsistent behaviour with PDFView.currentDestination and PDFView.go(to destination: PDFDestination)

Note: The question remains unsolved for now; the marked answer provides a good workaround - it works while the application is still open. Answers are still welcomed!
Background
I'm currently developing an app that consists of a full-screen PDFView, and I want the program to remember the position in the document before the view is dismissed so the user can pick up where they've left.
Implementation
A simplified version of the app can be understood as a PDF Viewer using PDFKit.PDFView. The storyboard consists of an UIView that's connected to a PDFView class, DocumentView (which conforms to UIViewController). The view is initialised through the following process:
In viewDidLoad:
let PDF: PDFDocument = GetPDFFromServer()
DocumentView.document = PDF!
DocumentView.autoScales = true
... (other settings)
// Set PDF Destination
let Destination: PDFDestination = GetStoredDestination()
// Code with issues
DocumentView.go(to: Destination)
In viewWillDisappear:
StoreDestination(DocumentView.currentDestination)
The Issue & Tests
I realised that the code does not work as expected; the view does not return to its previous location.
Through debugging, I realised that this might be due to the inconsistent behaviour of DocumentView.go(to destination: PDFDestination) and DocumentView.currentDestination.
To ensure the bug is not introduced by errors while storing the location, the following code is used to verify the issue, with a multi-page document:
In viewDidLoad
Timer.scheduledTimer(withTimeInterval: 1, repeats: true, block: { _ in
DispatchQueue.main.async {
self.DocumentView.go(to:self.DocumentView.currentDestination!)
}
})
Expected & Observed behaviour
Expected: The location of the document should not change - the code is going to its current destination every 1 second which should have no effects. as "currentDestination" should be the "current destination of the document, per docs")
Observed: Upon execution, the page would spontaneously scroll down by a fixed offset.
The same outcome was observed on an iPadOS 14.5 simulator and an iPadOS 15 iPad Air (Gen 4).
What might have gone wrong?
It'd be great if somebody can help.
Cheers,
Lincoln
This question was originally published on the Apple Developer Forum over a week ago; No responses were heard for over a week, so I thought I might try my luck here on StackOverflow <3
I tried PDFView.go() for this scenario and I managed to get it work for some cases but found that it fails in some other scenarios such as with zoomed documents, changed orientations.
So going back to what you are trying to achieve,
I'm currently developing an app that consists of a full-screen
PDFView, and I want the program to remember the position in the
document before the view is dismissed so the user can pick up where
they've left.
this can be done from a different approach. With this approach, you need to always keep a reference to the PDFView you created. If the previous pdf needs to be loaded again, then you pass the PDFView instance you have to the viewController as it is. Otherwise you load the new pdf to the PDFView instance and pass it to the viewController.
DocumentViewController gets the PDFView when it gets initialized.
import UIKit
import PDFKit
protocol DocumentViewControllerDelegate: AnyObject {
func needsContinuePDF(continuePDF: Bool)
}
class DocumentViewController: UIViewController {
var pdfView: PDFView!
weak var delegate: DocumentViewControllerDelegate!
init(pdfView: PDFView, delegate: DocumentViewControllerDelegate){
self.pdfView = pdfView
self.delegate = delegate
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func loadView() {
super.loadView()
self.view.backgroundColor = .white
view.addSubview(pdfView)
NSLayoutConstraint.activate([
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)
])
}
override func viewWillDisappear(_ animated: Bool) {
delegate.needsContinuePDF(continuePDF: true)
}
}
You can initialize DocumentViewController like below. MainViewController has the responsibility for initializing PDFView.
import UIKit
import PDFKit
class MainViewController: UIViewController {
var pdfView: PDFView = PDFView()
var continuePreviousPDF = false
let button = UIButton(frame: .zero)
override func loadView() {
super.loadView()
button.setTitle("View PDF", for: .normal)
button.setTitleColor(.white, for: .normal)
button.backgroundColor = .systemBlue
button.addTarget(self, action: #selector(openDocumentView(_:)), for: .touchUpInside)
self.view.backgroundColor = .systemGray5
self.view.addSubview(button)
button.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
button.widthAnchor.constraint(equalToConstant: 100),
button.heightAnchor.constraint(equalToConstant: 50),
button.centerXAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerXAnchor),
button.centerYAnchor.constraint(equalTo: self.view.safeAreaLayoutGuide.centerYAnchor),
])
}
#objc func openDocumentView(_ sender: UIButton) {
//open a nee PDF if not continue previous one
if !self.continuePreviousPDF {
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.translatesAutoresizingMaskIntoConstraints = false
guard let path = Bundle.main.url(forResource: "sample copy", withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
}
let documentViewController = DocumentViewController(pdfView: pdfView, delegate: self)
self.present(documentViewController, animated: true, completion: nil)
}
}
extension MainViewController: DocumentViewControllerDelegate {
func needsContinuePDF(continuePDF: Bool) {
self.continuePreviousPDF = continuePDF
}
}

How to present a single page pdf document in PDFView so that the document is centred and fully presented?

I have an application that takes a photo of a document and then presented as a pdf in PDFView in a new UIViewController. The problem that I am having is that when the pdf document is presented, it is some what zoomed in and the PDFView does not show the full outline of the document by default. How do I accomplish this?
class ShowPDFViewController: UIViewController{
#IBOutlet weak var pdfPreview: PDFView!
var pdfDocument: PDFDocument!
override func viewDidLoad() {
super.viewDidLoad()
// PRESENT PDF DOCUMENT JUST CREATED
pdfPreview.document = pdfDocument
pdfPreview.autoScales = true
}
}
This is how the pdf document is currently presented default - often zoomed:
This is how I would like the document presented by default - full outline of document see:
I create the view from code the following way and it works (left and right side are aligned to superview left and right and height is scaled to fit)
var pdfView = PDFView(frame: view.frame)
pdfView.document = PDFDocument(url: Bundle.main.url(forResource: "doc"
, withExtension: "pdf")!)
pdfView.displayMode = .singlePage
pdfView.autoScales = true
view.addSubview(pdfView)
I have a idea.
you can create a ScrollView , add PDFView to it. put them to Controller.
you can change ScrollView.contentOffset for your need
Fix for pdfView.displayMode = .singlePageContinuous
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
DispatchQueue.main.async {
self.pdfView.subviews.forEach {
guard let scrollView = ($0 as? UIScrollView) else { return }
scrollView.setContentOffset(.zero, animated: false)
}
}
}

Swift iOS App how to display a UIView over a UITabBarController

I have an iOS App with 3 ViewController, on my first ViewController if the user isn't logged I show a popUp that cover all the screen with a black UIView (opacity 50%, like a UIAlertController) and 2 buttons (register or log in).
The problem is, the tabBar is always over my UIView and i would like to display over the tabBar... (I can't juste hide the tabBar because of the opacity, i still want to see the tabBar under my black opacity 50% view.
I tried with layer's zPosition but that's doesn't work.
Any idea ?
Thanks
You should add the view in window .
AppDelegate.swift :
class func getDelegate() -> AppDelegate
{
return UIApplication.shared.delegate as! AppDelegate
}
YourViewController.swift
// set frame to full windows size
your_popup_view.frame = AppDelgate.getDelegate().window?.frame;
// add view on the top current window
AppDelgate.getDelegate().window?.addSubview(your_popup_view)
This would be the correct way to do it:
/* Add dialog to main window */
guard let appDelegate = UIApplication.shared.delegate else { fatalError() }
guard let window = appDelegate.window else { fatalError() }
window?.addSubview(self)
window?.bringSubview(toFront: self)
window?.addSubview(**Your view**)
window?.endEditing(true)
You can try this:
class YourView: UIView {
//setup your view...
}
protocol YourViewPresenter {
func showYourView()
func hideYourView()
}
extension YourViewPresenter where Self: UIViewController {
func showYourView() {
let yourView = YourView()
view.addSubview(yourView)
//after adding newly view, need to resize it
yourView.fillSuperview()
}
func hideYourView() {
(view.subviews.filter { $0 is PVInvitePopupView }).first?.removeFromSuperview()
}
}
extension UIView {
func fillSuperView() {
guard let superView = superview else { return }
translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
leadingAnchor.constraint(equalTo: superView.leadingAnchor, constant: 0),
trailingAnchor.constraint(equalTo: superView.trailingAnchor, constant: 0),
topAnchor.constraint(equalTo: superView.topAnchor, constant: 0),
bottomAnchor.constraint(equalTo: superView.bottomAnchor, constant: 0)
])
}
}
And then just conform you TabBarController to this extension:
extension UITabBarController: YourViewPresenter {}
//...
Then, you could call showYourView() somewhere in your viewController, like:
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.tabBarController?.showYourView()
}

How to show FullScreenSlideshowViewController on ViewDidLoad

I am new to iOS development, I have created android app where app gets sets of images (sometimes over 100 images) from urls and loads into imageView with zoomEnabled Inside ViewPager.
Now I want to create same app for iOS, I have found this lib ImageSliderShow. Problem with this is it shows fullscreen ONLY when user did tap on selected image. I have been struggling to presentFullScreenController on viewDidLoad with no lock.
I only want image to be shown in fullScreen, example:
Select Category A From CategoryVC -> Loads ImageSlideVC, gets set of images from server, show in FullScreenView.
How can i achieve this? Adding this:
slideshow.presentFullScreenController(from: self)
on viewDidLoad didn't work:
#IBOutlet var slideshow: ImageSlideshow!
let kingfisherSource = [KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1447746249824-4be4e1b76d66?w=1080")!,
KingfisherSource(urlString: "https://images.unsplash.com/photo-1463595373836-6e0b0a8ee322?w=1080")!]
override func viewDidLoad() {
super.viewDidLoad()
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
I dont get any error, output is same as before (shows slideshow in smallview with no zoom)
Please help
EDIT
by doing in viewDidAppear, after split second view is loaded its loads into fullscreen. First its smallview then shows in fullscreen. I think i have to do something inside setImageInputs
this is what its looks like :
open func setImageInputs(_ inputs: [InputSource]) {
self.images = inputs
self.pageControl.numberOfPages = inputs.count
// in circular mode we add dummy first and last image to enable smooth scrolling
if circular && images.count > 1 {
var scImages = [InputSource]()
if let last = images.last {
scImages.append(last)
}
scImages += images
if let first = images.first {
scImages.append(first)
}
self.scrollViewImages = scImages
} else {
self.scrollViewImages = images
}
reloadScrollView()
layoutScrollView()
layoutPageControl()
setTimerIfNeeded()
}
try to do it in viewDidAppear
#IBOutlet var slideshow: ImageSlideshow!
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
slideshow.setImageInputs(kingfisherSource)
slideshow.presentFullScreenController(from: self)
}
Presenting viewController in ViewDidLoad is a bad practice.

MBProgressHUD with Swift, Fabric & TwitterKit

I'm using Fabric and TwitterKit to show a timeline at my Table View. At my code I'm using a dispatch_async function. I want to start the MBProgressHUD and after the timeline was loaded I want to disable the HUD. Unfortunately my HUD is appearing only for less than one second and the timeline is appearing about two seconds later. No matter how good or bad my internet connection is. I want to show the HUD as long as the timeline is loading.
This is my code:
override func viewDidLoad() {
super.viewDidLoad()
let ProgressHUD = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
self.tableView.separatorStyle = UITableViewCellSeparatorStyle.None
let client = TWTRAPIClient()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)) {
self.dataSource = TWTRUserTimelineDataSource(screenName: "fabric", APIClient: client)
self.showTweetActions = true
//Dark Timeline Theme
TWTRTweetView.appearance().theme = .Dark
TWTRTweetView.appearance().linkTextColor = UIColor.redColor()
dispatch_async(dispatch_get_main_queue()) {
ProgressHUD.hide(true)
}
}
}
I've done the same as:
MBProgressHUD.showHUDAddedTo(self.view, animated: true) //for showing
MBProgressHUD.hideHUDForView(self.view, animated: true) //for hiding
No need of that ProgressHud variable.
Well, i changed the framework to JGProgressHUD. It's working perfectly. No more problem here! Thank you for your help –

Resources