AirPrint multiple MVCs in swift - swift

I am building a form style app that allows the users to fill in textfields. once they fill up the page it will continue onto a second view. This will be about 5 pages worth of patient forms. when they get to the last page I want to have a print button that prints all 5 or 6 pages they have just filled out. I am trying to use the AirPrint code that I have recently found on here, but it will only let me do the current view.
extension UIView {
func toImage() -> UIImage {
UIGraphicsBeginImageContextWithOptions(bounds.size, false, UIScreen.main.scale)
// drawHierarchy(in: self.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image!
}
}
let pInfo = UIPrintInfo(dictionary:nil)
pInfo.outputType = UIPrintInfoOutputType.general
pInfo.jobName = "Print Patient Forms"
//Set up print controller
let printController = UIPrintInteractionController.shared
printController.printInfo = pInfo
//Assign a UIMage version of my UIView as a printing Item
printController.printingItem = self.view
var pInfo : UIPrintInfo = UIPrintInfo.printInfo()
pInfo.orientation = UIPrintInfoOrientation.portrait
let formatter = UIMarkupTextPrintFormatter(markupText: textView.text!)
printController.showsNumberOfCopies = false
printController.showsPageRange = true
printController.printFormatter = formatter printController.present(animated: true, completionHandler: nil)
How do I go about letting this print all the pages?

Related

AdjustsFontSizeToFitWidth equivalent for iOS15 UIButton

I'm using UIKit, not SwiftUI. Previously, I used setAttributedTitle and I also set AdjustsFontSizeToFitWidth to true, and the font size shrinks to match the button width. I can't seem to match this behavior with the new button configurations and swift's AttributedString.
Here's what I have:
var config = UIButton.Configuration.filled()
config.imagePadding = 5
config.imagePlacement = .trailing
config.image = UIImage(systemName: "ellipsis.circle",
withConfiguration: UIImage.SymbolConfiguration(scale: .medium))
config.titlePadding = 10
config.cornerStyle = .capsule
let handler: UIButton.ConfigurationUpdateHandler = { button in
button.configuration?.background.backgroundColor = Colors.specLabel
button.configuration?.baseForegroundColor = Colors.greyText
}
myButton.configuration = config
myButton.configurationUpdateHandler = handler
And then
guard let attributedString = try? AttributedString(markdown: "hello **hello** hello") else { return }
myButton.configuration?.attributedTitle = attributedString
Every time, I get a multi-line button. Setting numberOfLines, lineBreakMode and adjustsFontSizeToFitWidth is all ignored. Any ideas?

How do I make a PDFView start at the top of the document?

Currently I am using a PDFView in my swift application, however when I instantiate the view controller that uses the PDFView, it always opens the pdf slightly scrolled down. For example:
The first image is how it opens, and the second image is the way I would like it to open. I can simply scroll up to have it look like the image on the right, however I wanted to know if there was any way of automatically doing it.
Here is the code I am currently using:
override func viewDidLoad() {
let pdfView = PDFView()
pdfView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(pdfView)
pdfView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
pdfView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
pdfView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
pdfView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
pdfView.autoScales = true
pdfView.displayMode = .singlePageContinuous
pdfView.displayDirection = .vertical
guard let url = item?.url else {
return
}
guard let path = Bundle.main.url(forResource: url, withExtension: "pdf") else { return }
if let document = PDFDocument(url: path) {
pdfView.document = document
}
}
I have the same issue and I solve it adding the following line:
override func viewDidLoad() {
super.viewDidLoad()
// this line
self.edgesForExtendedLayout = []
let pdfView = PDFView()()
...
}
That line stops your view going under the navigation bar
I had the same issue with my PDF Document.
It looks like the document within the PDF Viewer simply ignored the top-anchor of the PDF Viewer and ended up favouring the top of the device and thus you have the top part of the document under the navigation bar.
The way I solved it was to add a UIView to the view-controller and anchor it all around to the 'safeAreaLayoutGuide' of the view-controller, then add the PDFView to that UIView and pin it to all the edges. Seems like a long winded approach, but it works.
The code below shows the basic implementation.
lazy var pdfView: PDFView = {
let pdf = PDFView(frame: self.view.bounds)
pdf.displayMode = .singlePageContinuous
pdf.displayDirection = .vertical
pdf.scaleFactor = pdf.scaleFactorForSizeToFit
pdf.autoScales = true
pdf.translatesAutoresizingMaskIntoConstraints = false
pdf.setValue(true, forKey: "forcesTopAlignment")
return pdf
}()
let pdfContentView = UIView()
view.addSubview(pdfContentView)
pdfContentView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([pdfContentView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
pdfContentView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
pdfContentView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
pdfContentView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor)])
pdfContentView.addSubview(pdfView)
NSLayoutConstraint.activate([pdfView.topAnchor.constraint(equalTo: pdfContentView.topAnchor),
pdfView.bottomAnchor.constraint(equalTo: pdfContentView.bottomAnchor),
pdfView.widthAnchor.constraint(equalTo: pdfContentView.widthAnchor),
pdfView.centerXAnchor.constraint(equalTo: pdfContentView.centerXAnchor)])

ios - Swift 4: How to remove UIImageViews when UIImageView.animationImages is finished?

I am using .animationImages for UIImageViews in my project (it's a game). When the user presses a hint button I programmatically create a variable number of UIImageViews that will appear on screen and play the same image sequence once. I need to remove the UIImageViews from the view when the animation has stopped but I can't figure out how to do this.
I've been through the swift documentation for both .animationImages and .startAnimating but it's literally one paragraph and one line, neither of which is in relation to a completion or finish notifications etc. Any help will be much appreciated.
private func hideAnswerButtonsHint() {
// Clear all of the answer boxes that aren't hint locked
resetAnswerBoxes()
var imageArray = [UIImage]()
var remainingLetters = [String]()
for imageCount in 1...8 {
let imageName = "green_smoke_puff_0\(imageCount)"
let image = UIImage(named: imageName)!
imageArray.append(image)
}
for answerBox in answerBoxArray where answerBox.hintLock == false {
if let letter = answerBoxDict[answerBox.tag] {
remainingLetters.append(letter)
}
}
for answerButton in answerButtonArray where answerButton.hintLock == false {
if let index = remainingLetters.index(of: answerButton.titleText) {
answerButton.decompress()
remainingLetters.remove(at: index)
} else {
let frame = answerButton.superview?.convert(answerButton.frame.origin, to: nil)
let imageView = UIImageView()
imageView.animationImages = imageArray
imageView.animationDuration = 0.5
imageView.animationRepeatCount = 1
imageView.frame = CGRect(x: frame!.x, y: frame!.y, width: answerButton.frame.width, height: answerButton.frame.height)
view.addSubview(imageView)
imageView.startAnimating()
answerButton.compress()
answerButton.hintLock = true
}
}
}
UIImageView is an NSObject so you can always use KVO to watch its properties:
var observation: NSKeyValueObservation?
override func viewDidLoad() {
super.viewDidLoad()
observation = imageView.observe(\.isAnimating) { [weak self] imageView, change in
if let value = change.newValue,
value == true {
imageView.removeFromSuperview()
self?.observation = nil
}
}
}

Taking a snapshot of UIImageView instead of main View

I have in my app in my View a Button for taking snapshot with below code
open func takeScreenshot(_ shouldSave: Bool = true) -> UIImage? {
var screenshotImage :UIImage?
let layer = UIApplication.shared.keyWindow!.layer
let scale = UIScreen.main.scale
UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, scale);
guard let context = UIGraphicsGetCurrentContext() else {return nil}
layer.render(in:context)
screenshotImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let image = screenshotImage, shouldSave {
UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
}
return screenshotImage
}
But in the same View is UIImageView and I want to take a snapshot of this UIImageView. How should I change the code? When I take a snapshot with this code, it will save only black screen with button for play/pause video and with button for taking snapshot.
Instead you using
let layer = UIApplication.shared.keyWindow!.layer
Just use your imageview Layer
let layer = YourImageView.layer

iOS 11 doesn't grab screen from MKMapView

I have an app that displays the locations the user has walked on an MKMapView. When the user leaves the map view the app grabs the screen and saves the image on disk. Up til iOSS 10.3 this method was always successful. With iOS 11.0 the screen grab is a blank image. I get no notification from xcode that there were some changes and that I need to adjust the code.
Interestingly, screen grabs from text pages are still grabbed and saved successfully.
Did anyone encounter the same problem and got the solution?
The code that has always been successful up til now, is:
override func viewWillDisappear(_ animated: Bool) {
//Set the full file name under which the track will be saved.
let fileBaseName = self.imageName.appending(String(describing: (self.display?.trackDate)!))
let fileFullName = fileBaseName.appending(".png")
//Check if the image already has been saved
if !AuxiliaryObjects.shared.getImageFileName(with: fileFullName ) {
//Create the sizes of the capture
let screenRect = self.trackMapView.frame
let screenSize = screenRect.size
let screenScale = UIScreen.main.scale
var grabRect = self.trackMapView.convertRegion(self.mapRegion, toRectTo: self.view)
var heightAdjustment : CGFloat = 0.0
//Grab the image from the screen
UIGraphicsBeginImageContextWithOptions(screenSize, false, screenScale)
self.trackMapView.drawHierarchy(in: screenRect, afterScreenUpdates: true)
let myImage = UIGraphicsGetImageFromCurrentImageContext()
grabRect.origin.x *= (myImage?.scale)!
grabRect.origin.y *= (myImage?.scale)!
grabRect.size.width *= (myImage?.scale)!
grabRect.size.height *= (myImage?.scale)!
let grabImage = (myImage?.cgImage)!.cropping(to: grabRect)
let mapImage = UIImage(cgImage: grabImage!)
UIGraphicsEndImageContext()
AuxiliaryObjects.shared.save(image: mapImage, with: fileFullName, and: self.imageName)
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
} else {
self.display?.displayImage = AuxiliaryObjects.shared.getImage(with: fileFullName, with: self.tableImageRect)!
}
}
I submitted a code level support request at Apple to get the answer to the question. Apple does not support the use of drawHierarhy in grabbing a MapKit screen. The way to go is using the MKMapSnapshotter utility to create an MKMapSnapshot and then draw in the lines and annotations by converting all the map coordinates to view coordinates.
Since this gave me some problems with getting the a mirrored image and translating the coordinates properly, I decided to use the layer method render(in: CGContext) this provided me a well functioning very efficient screen grab.
func creatSnapshot(with fileName: String) {
UIGraphicsBeginImageContextWithOptions(self.trackMapView.frame.size, false, UIScreen.main.scale)
let currentContext = UIGraphicsGetCurrentContext()
self.trackMapView.layer.render(in: currentContext!)
let contextImage = (UIGraphicsGetImageFromCurrentImageContext())!
UIGraphicsEndImageContext()
let region = self.trackMapView.region
var cropRect = self.trackMapView.convertRegion(region, toRectTo: self.trackMapView.superview)
cropRect.origin.x *= contextImage.scale
cropRect.origin.y *= contextImage.scale
cropRect.size.height *= contextImage.scale
cropRect.size.width *= contextImage.scale
let cgMapImage = contextImage.cgImage?.cropping(to: cropRect)
let mapImage = UIImage(cgImage: cgMapImage!)
AuxiliaryObjects.shared.save(image: mapImage, with: fileName, and: self.imageName)
self.displayTrack?.displayImage = AuxiliaryObjects.shared.getImage(with: fileName, with: self.tableImageRect)!
NotificationCenter.default.post(name: self.imageSavedNotification, object: self)
}