Crash on UICollectionViewCell with JWVideoView - Swift - ios

A ViewController has a UICollectionView. One of the cells contains JWVideoView. The app is frequently crashing on prepareForReuse in this cell.
There is no valuable info in the log. So I am having trouble figuring out the reason for the crash.
I've created a project example that demonstrates the crash. You can find it https://github.com/fuxlud/JWExample
If the link between the cell and the videoView is removed, the crash will not happen.
import UIKit
class VideoArticleElementCollectionViewCell: UICollectionViewCell {
// MARK: - Properties
public var imageURL: String? { didSet { videoView?.imageURL = imageURL } }
public var videoId: String? { didSet { videoView?.videoId = videoId } }
#IBOutlet private var videoView: JWVideoView?
// MARK: - Reuse
override func prepareForReuse() {
super.prepareForReuse() // Crashing here! (Thread 1: EXC_BAD_ACCESS (code=1, address=0x7e8))
videoView?.stopPlayingVideo()
}
deinit {
videoView?.stopPlayingVideo()
}
}
import UIKit
class JWVideoView: UIView, JWPlayerDelegate {
// MARK: Properties
public var imageURL: String?
public var videoId: String? { didSet { setupPlayer() } }
private var jwPlayer: JWPlayerController?
private let jwPlayerURL = "https://content.jwplatform.com/manifests/"
private var didPause = false
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
convenience init() {
self.init(frame: CGRect.zero)
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setup()
}
// MARK: - Setup
private func setup() {}
private func setupPlayer() {
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
// configuration.premiumSkin = JWPremiumSkinGlow
configuration.image = imageURL
jwPlayer = JWPlayerController(config: configuration)
if let player = jwPlayer {
player.forceFullScreenOnLandscape = true
player.forceLandscapeOnFullScreen = true
player.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
player.view?.frame = bounds
player.delegate = self
player.volume = 0.0
if let view = player.view { addSubview(view) }
}
}
// MARK: - Orientation
private func enableAllOrientation(enable: Bool) {
if let delegate = UIApplication.shared.delegate as? AppDelegate {
// delegate.shouldEnableLandscape = enable
}
}
// MARK: API
public func stopPlayingVideo() {
enableAllOrientation(enable: false)
if jwPlayer != nil {
jwPlayer!.stop()
}
}
// MARK: - JWPlayerDelegate
internal func onFullscreen(_ status: Bool) {
if status == false {
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
}
internal func onPlayAttempt() {
if jwPlayer != nil {
enableAllOrientation(enable: true)
}
}
internal func onPlay(_ oldValue: String) {
if didPause {
didPause = false
}
}
internal func onPause(_ oldValue: String) {
didPause = true
}
internal func onComplete() {
}
}

Based on your example project a saw the following issue inside your JWVideoView class: everytime you setting the videoId property it initiliaze the jwPlayer again, and also readds this view again to the stack.
1. Solution (remove the playerView and set the player to nil):
private func setupPlayer() {
jwPlayer?.view?.removeFromSuperview()
jwPlayer = nil
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
configuration.image = imageURL
jwPlayer = JWPlayerController(config: configuration)
jwPlayer?.forceFullScreenOnLandscape = true
jwPlayer?.forceLandscapeOnFullScreen = true
jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
jwPlayer?.view?.frame = bounds
jwPlayer?.delegate = self
jwPlayer?.volume = 0.0
if let view = jwPlayer?.view {
addSubview(view)
}
}
2. Solution (keep the player and the view instance and reset the configuration of the player)
private func setupPlayer() {
guard let videoId = self.videoId else { return }
let playerURL = jwPlayerURL + videoId + ".m3u8"
let configuration: JWConfig = JWConfig(contentURL: playerURL)
configuration.controls = true
configuration.autostart = true
configuration.image = imageURL
if jwPlayer == nil {
jwPlayer = JWPlayerController(config: configuration)
jwPlayer?.forceFullScreenOnLandscape = true
jwPlayer?.forceLandscapeOnFullScreen = true
jwPlayer?.view?.autoresizingMask = [.flexibleHeight, .flexibleWidth]
jwPlayer?.view?.frame = bounds
jwPlayer?.delegate = self
jwPlayer?.volume = 0.0
if let view = jwPlayer?.view {
addSubview(view)
}
}else{
//reset the configuration of the player here. but i dont now how this is possible with jwPlayer
}
}

Related

Can't show part of data in a cell

I try to show data from API Response. I have json and DataService.
When ViewController is load, my presenter give away data from my Service.
I don't know why, but I make two request to my service. First time I get only one empty cell and the second time I get other data.
Image from view hierarchy:
My DataService:
class DataService {
func getRouts(completion: #escaping (APIResponse) -> Void?) {
let urlString = "https://travel.wildberries.ru/statistics/v1/cheap"
guard let url = URL(string: urlString) else { return }
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
do {
let jsonResults = try JSONDecoder().decode(APIResponse.self, from: data)
completion(jsonResults)
}
catch {
print{error}
}
}
task.resume()
}
}
My presenter
protocol PresenterProtocol: AnyObject {
func viewDidLoad()
}
final class ListTicketsModulPresenter: PresenterProtocol {
let dataService = DataService()
weak var listTicketsViewController: ListTicketsViewController?
func viewDidLoad() {
dataService.getRouts { [weak self] results in
DispatchQueue.main.async {
print(results)
self?.listTicketsViewController?.configure(with: results)
}
}
}
}
My ViewController:
final class ListTicketsViewController: UIViewController, ListTicketsViewControllerProtocol {
var presenter: PresenterProtocol
let tableView: UITableView = {
let tableView = UITableView()
tableView.backgroundColor = UIColor.white
tableView.separatorColor = .white
tableView.allowsSelection = false
tableView.translatesAutoresizingMaskIntoConstraints = false
return tableView
}()
var numberOfRows: Int?
var startCityNameArray: [String]?
var endCityNameArray: [String]?
var startDateArray: [String]?
var endDateArray: [String]?
var price: [Int]?
...
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
view.addSubview(tableView)
setupTableView()
presenter.viewDidLoad()
}
func setupTableView() {
tableView.delegate = self
tableView.dataSource = self
tableView.register(ListTicketsModulCell.self, forCellReuseIdentifier: "cellId")
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor).isActive = true
tableView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor).isActive = true
tableView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor).isActive = true
tableView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor).isActive = true
}
//Functions
func configure(with model: APIResponse) {
self.startCityNameArray = model.data.map { $0.startCity }
self.endCityNameArray = model.data.map { $0.endCity }
self.numberOfRows = model.data.count
self.startDateArray = model.data.map { $0.startDate }
self.endDateArray = model.data.map { $0.endDate }
self.price = model.data.map { $0.price }
self.tableView.reloadData()
}
...
}

Data Sharing Between My App and App Extensions

I transfer data from the sharing extension to my main application with UserDefaults and open the application (goToApp()) after hitting the "post" button. However, the view of my app is not redrawn and the text remains the same "Share Extension Example". Here's how I'm trying to do it:
class ShareViewController: SLComposeServiceViewController {
private var textString: String?
override func isContentValid() -> Bool {
if let currentMessage = contentText {
self.textString = currentMessage
}
return true
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func didSelectPost() {
UserDefaults.standard.set(self.textString!, forKey: "text")
gotoApp()
self.extensionContext!.completeRequest(returningItems: [], completionHandler: nil)
}
func gotoApp() {
guard let url = URL(string: "example://") else { return }
let selectorOpenURL = sel_registerName("openURL:")
var responder: UIResponder? = self
while responder != nil {
if responder?.responds(to: selectorOpenURL) == true {
responder?.perform(selectorOpenURL, with: url)
}
responder = responder?.next
}
}
}
And the project to which I am trying to transfer data:
class ViewController: UIViewController {
private let mainVStack = UIStackView()
private let backgroundView = UIImageView()
private let titleLabel = UILabel()
override func viewDidLoad() {
super.viewDidLoad()
configureMainStack()
configureTitleLabel()
}
}
// MARK: - UI Elements
private extension ViewController {
func configureMainStack() {
mainVStack.distribution = .fillProportionally
mainVStack.embed(asSubviewTo: view, inset: 40)
}
func configureTitleLabel() {
titleLabel.textAlignment = .center
titleLabel.textColor = .blue
if let text = UserDefaults.object(forKey: "text") as? String {
titleLabel.text = text
} else {
titleLabel.text = "Share Extension Example"
}
let titleContainerView = UIView()
titleLabel.embedIn(titleContainerView, hInset: 0, vInset: 100)
mainVStack.addArrangedSubview(titleContainerView)
}
}

AVFragmentedAssetMinder not working on iPhone device but works on simulator

We have fragmented movie data that comes through WebSecureSocket (wss://). We are writing that into a temp.mp4 and simultaneously playing the fragments that got written into the file. So, we used AVFragmentedAsset and AVFragmentedAssetMinder for this. It works as expected in the simulator. But in the device, doesn't update the duration of the asset and doesn't post the .AVAssetDurationDidChange notification. Not sure what could be the issue. Already spent 2 days figuring out this but no luck. Can someone help me with this please,
Implementation..
public class WssPlayerSource: WebSocketDelegate {
static let ASSET_MINDER = AVFragmentedAssetMinder()
private let socketClient: WebSocketStreamingClient
private var tempMovieFile:FileHandle? = nil
private var startedPlay = false
private var fragmentedAsset: AVFragmentedAsset? = nil
let player = AVPlayer()
init(withUrl url: URL) {
socketClient = WebSocketStreamingClient(wssUrl: url)
socketClient.delegate = self
do {
self.tempMovieFile = try getTemporaryMovieFileWriter()
}catch {
print("Error opening file")
}
NotificationCenter.default.addObserver(self, selector: #selector(onVideoUpdate), name: .AVAssetDurationDidChange, object: nil)
socketClient.connect()
}
// Socket delegate
public func didReceive(event: WebSocketEvent, client: WebSocket) {
switch event {
case .binary(let data):
self.tempMovieFile?.write(data)
if !startedPlay {
startedPlay = true
DispatchQueue.global().async { [weak self] in
self?.didReceivedInitialData()
}
}
break;
default:
break;
}
}
func didReceivedInitialData() {
fragmentedAsset = AVFragmentedAsset(url: getTemporaryMovieFile()!)
fragmentedAsset?.loadValuesAsynchronously(forKeys: ["duration", "containsFragments", "canContainFragments"], completionHandler: {
WssPlayerSource.ASSET_MINDER.mindingInterval = 1
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(self.fragmentedAsset!)
self.player.replaceCurrentItem(with: AVPlayerItem(asset: self.fragmentedAsset!))
self.player.play()
})
}
#objc
func onVideoUpdate() {
print("Video duration updated..")
// This is not called in device.. but in simulator it works as expected
}
func stop() {
socketClient.forceDisconnect()
NotificationCenter.default.removeObserver(self)
if let asset = self.fragmentedAsset {
WssPlayerSource.ASSET_MINDER.addFragmentedAsset(asset)
}
}
}
And
class ViewController: UIViewController {
#IBOutlet var playerView:PlayerView!
private static let SOCKET_URL = "wss://..."
private var playerSource:WssPlayerSource? = nil
private var playerLayer:AVPlayerLayer? = nil
override func viewDidLoad() {
super.viewDidLoad()
self.playerSource = WssPlayerSource(withUrl: URL(string: ViewController.SOCKET_URL)!)
self.playerLayer = AVPlayerLayer(player: self.playerSource!.player)
self.playerLayer?.videoGravity = .resize
self.playerView.layer.addSublayer(self.playerLayer!)
}
}

swift protocol delegate always return nil

I have this protocol delegate defined in my View Controller:
protocol PickerDelegate : NSObjectProtocol {
func updateMessage(meesage: String)
}
and then I called this in my View Controller:
class GradingController: UIViewController, UIPickerViewDataSource, UIPickerViewDelegate, SDataGridDataSourceHelperDelegate, SLAIssuedFinalGradingDelegate, CityApprovalIssuedDelegate, CityCommentReceivedDelegate, DepositReceivedDelegate, UIPopoverPresentationControllerDelegate {
var pickerDelegate: PickerDelegate?
}
And then I am calling my method inside the protocol delegate (this is where its nil):
func popoverPresentationControllerDidDismissPopover(_ popoverPresentationController: UIPopoverPresentationController) {
let controller = popoverPresentationController.presentedViewController as! CommentsController
pickerDelegate?.updateMessage(meesage: controller.commentView.text)
}
And I am using this delegate in my custom class:
class TextCell: SDataGridCell, PickerDelegate {
var dataGrid: ShinobiDataGrid?
private var _commentText = ""
private var label: UILabel?
var commentText: String {
get {
return _commentText
}
set(commentText) {
if(commentText != "")
{
label?.text = commentText
}
else
{
label?.text = "N/A"
}
}
}
override init(reuseIdentifier identifier: String!) {
super.init(reuseIdentifier: identifier)
label = UILabel()
label?.font = UIFont.systemFont(ofSize: 15)
label?.frame = CGRect(x: 0, y: 0, width: 200, height: 32)
addSubview(label!)
let pickerViewController = GradingController()
pickerViewController.pickerDelegate = self
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init(frame: CGRect) {
super.init(frame: frame)
}
override func respondToEditEvent() {
if dataGrid?.delegate.responds(to: #selector(SDataGridDelegate.shinobiDataGrid(_:shouldBeginEditingCellAtCoordinate:))) ?? false {
if dataGrid?.delegate.shinobiDataGrid!(dataGrid, shouldBeginEditingCellAtCoordinate: coordinate) == false {
return
}
}
if dataGrid?.delegate.responds(to: #selector(SDataGridDelegate.shinobiDataGrid(_:willBeginEditingCellAtCoordinate:))) ?? false {
dataGrid?.delegate.shinobiDataGrid!(dataGrid, willBeginEditingCellAtCoordinate: coordinate)
}
}
func updateMessage(meesage: String) {
commentText = meesage
}
}
But the updateMessage method is not being called, my delegate is nil in my View Controller when I try to use it in popoverPresentationControllerDidDismissPopover but it always return nil :(
What am I doing wrong?
This is the TextCell in GradingController:
func dataGridDataSourceHelper(_ helper: SDataGridDataSourceHelper!, populateCell cell: SDataGridCell!, withValue value: Any!, forProperty propertyKey: String!, sourceObject object: Any!) -> Bool {
let cellDataObj = object as? GradingData
if(propertyKey == "GradingRepair")
{
let textCell = cell as? TextCell
textCell?.dataGrid = self.grid
textCell?.commentText = (cellDataObj?.GradingRepair)!
return true
}
return false
}
Consider what this code does:
let pickerViewController = GradingController() // 1
pickerViewController.pickerDelegate = self // 2
// 3
You create a completely new GradingController.
You assign the GradingController a pickerDelegate.
Nothing. So you throw the GradingController away. Your code thus has no effect on anything.
What you need to do is to assign a pickerDelegate to the actual GradingController that's in your interface. But that's not what you did.

how i can add a text on pdf?

I found some code to show PDF file and, the code has a pencil to draw on pdf but I want to change the pencil to text. When I click on button I can write a text and move the text anywhere on view
I want to add text and change the size and move it as I want.
//
// ViewController.swift
// PDFTest
//
// Created by Prashoor Chitnis on 22/11/17.
// Copyright © 2017 InfoBeans LLC. All rights reserved.
//
import UIKit
import PDFKit
struct PAIRAnn {
var drawing: PDFAnnotation;
var button: PDFAnnotation;
var currentPage: PDFPage;
}
class ViewController: UIViewController, PDFViewDelegate, DrawEventListener {
#IBOutlet weak var closeBtn: UIBarButtonItem!
#IBOutlet weak var saveBtn: UIBarButtonItem!
#IBOutlet weak var undoBtn: UIBarButtonItem!
#IBOutlet weak var pencil: UIBarButtonItem!
var selectedAnnotation = [PAIRAnn]()
var position: CGPoint?
var currentPage : PDFPage?
var pView: PDFView?
var pdfView : PDFView {
get {
if pView == nil {
pView = PDFView(frame: self.view.frame)
pView?.delegate = self
}
return pView!
}
}
var fadeView: UIView?
var messageView : UIView {
get {
if fadeView == nil {
fadeView = UIView(frame: CGRect(x: (self.view.frame.size.width - 300)/2, y: 30, width: 300, height: 55))
var frm = (fadeView?.bounds)!
frm.origin.y = 15
frm.size.height = 25
let text = UILabel(frame: frm)
text.textAlignment = .center
text.text = "Double-Click on any spot to begin"
text.font = UIFont.systemFont(ofSize: 16)
fadeView?.addSubview(text)
fadeView?.layer.cornerRadius = 27.5
fadeView?.layer.borderWidth = 1.5
fadeView?.backgroundColor = UIColor.white.withAlphaComponent(0.8)
}
return fadeView!
}
}
var drawVw: DrawScreen?
var drawView : DrawScreen {
get {
if drawVw == nil {
drawVw = DrawScreen(frame: self.view.frame)
drawVw?.listener = self
}
return drawVw!
}
}
private var gesture: UITapGestureRecognizer?
private var gestureRecognizer: UITapGestureRecognizer {
get {
if gesture == nil {
gesture = UITapGestureRecognizer.init(target: self, action: #selector(addDrawing(sender:)))
gesture?.numberOfTapsRequired = 2
}
return gesture!
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.closeBtn.isEnabled = false
self.saveBtn.isEnabled = false
self.undoBtn.isEnabled = false
self.pencil.isEnabled = true
self.view.insertSubview(self.pdfView, at: 0)
let url = Bundle.main.url(forResource: "EN8", withExtension: "pdf")
self.pdfView.document = PDFDocument.init(url: url!)
}
#IBAction func addPencil(_ sender: UIBarButtonItem) {
self.pencil.isEnabled = false
self.view.insertSubview(self.messageView, aboveSubview: self.pdfView)
self.pdfView.addGestureRecognizer(self.gestureRecognizer)
}
func removeMessage() {
if self.messageView.superview == nil {
return
}
UIView.animate(withDuration: 0.4, animations: {
self.messageView.transform = CGAffineTransform.init(translationX: 0, y: -70)
}) { (success) in
self.messageView.removeFromSuperview()
self.messageView.transform = CGAffineTransform.identity
}
}
#objc func addDrawing(sender: UITapGestureRecognizer) {
self.removeMessage()
self.position = sender.location(in: self.view)
self.view.insertSubview(self.drawView, at: 1)
currentPage = self.pdfView.currentPage
self.pdfView.go(to: currentPage!)
self.pencil.isEnabled = false
self.undoBtn.isEnabled = false
self.saveBtn.isEnabled = false
self.closeBtn.isEnabled = true
}
func pdfViewWillClick(onLink sender: PDFView, with url: URL) {
var checkVal = url.absoluteString
checkVal = checkVal.replacingOccurrences(of: "path://", with: "")
var draw: PDFAnnotation?
for ann in self.allLinks() {
let value = "\(ann.value(forAnnotationKey: PDFAnnotationKey(rawValue: "Link"))!)"
if checkVal == value {
draw = ann
break
}
}
var currpage: PDFPage? = nil
var button: PDFAnnotation?
for (btn, page) in self.allButtons() {
let value = "\(btn.value(forAnnotationKey: PDFAnnotationKey(rawValue: "_Path"))!)"
if checkVal == value {
currpage = page
button = btn
break
}
}
if draw != nil && button != nil && currpage != nil {
let color = draw?.value(forAnnotationKey: PDFAnnotationKey(rawValue: "_Color")) as! String
switch (color) {
case "red":
draw?.color = UIColor.red
break
default:
break
}
currpage?.removeAnnotation(button!)
self.editButtonItem.isEnabled = false
self.undoBtn.isEnabled = true
self.pencil.isEnabled = false
self.selectedAnnotation.append(PAIRAnn.init(drawing: draw!, button: button!, currentPage: currpage!))
}
}
#IBAction func undoAction(_ sender: Any) {
self.removeMessage()
if self.selectedAnnotation.count > 0 {
for pairs in self.selectedAnnotation {
pairs.drawing.color = UIColor.clear
pairs.currentPage.addAnnotation(pairs.button)
}
self.selectedAnnotation = []
self.closeBtn.isEnabled = false
self.saveBtn.isEnabled = false
self.undoBtn.isEnabled = false
self.pencil.isEnabled = true
}
else {
self.drawView.undo()
}
}
#IBAction func removeDrawing(sender: Any) {
self.removeMessage()
self.pdfView.removeGestureRecognizer(self.gestureRecognizer)
self.drawView.removeFromSuperview()
self.closeBtn.isEnabled = false
self.saveBtn.isEnabled = false
self.undoBtn.isEnabled = false
self.pencil.isEnabled = true
}
func drawDidBegin() {
self.undoBtn.isEnabled = true
self.saveBtn.isEnabled = true
}
func drawingGotEmpty() {
self.undoBtn.isEnabled = false
self.saveBtn.isEnabled = false
}
#IBAction func saveDrawing(_ sender: Any) {
self.pdfView.removeGestureRecognizer(self.gestureRecognizer)
self.currentPage = self.pdfView.page(for: self.drawView.touches[0].path.first!, nearest: true)
var arrayPath = [UIBezierPath]()
for touches in self.drawView.touches {
let bPth = UIBezierPath.init()
let point = self.pdfView.convert(touches.path.first!, to: self.currentPage!)
bPth.move(to: point)
if touches.path.count > 1 {
for i in 1...touches.path.count-1 {
let tch = self.pdfView.convert(touches.path[i], to: self.currentPage!)
bPth.addLine(to: tch)
}
}
arrayPath.append(bPth)
}
let ann = PDFAnnotation.init(bounds: (self.currentPage?.bounds(for: self.pdfView.displayBox))!, forType: PDFAnnotationSubtype.ink, withProperties: nil)
ann.shouldDisplay = false
for bPth in arrayPath {
ann.add(bPth)
}
ann.color = UIColor.clear
let stamp = "PAGE_\(Date.init().timeIntervalSince1970)"
ann.setValue(stamp, forAnnotationKey: PDFAnnotationKey.init(rawValue: "Link"))
ann.setValue("red", forAnnotationKey: PDFAnnotationKey.init(rawValue: "_Color"))
self.currentPage?.addAnnotation(ann)
let linkPoint = self.pdfView.convert(self.position!, to: self.currentPage!)
let linkAnn = PDFAnnotation.init(bounds: CGRect(x: linkPoint.x - 20, y: linkPoint.y - 12, width: 40, height: 24), forType: PDFAnnotationSubtype.widget, withProperties: nil)
linkAnn.widgetFieldType = .button
linkAnn.widgetDefaultStringValue = "Open"
linkAnn.widgetControlType = .pushButtonControl
linkAnn.action = PDFActionURL(url: URL(string: "path://\(stamp)")!)
linkAnn.setValue(stamp, forAnnotationKey: PDFAnnotationKey.init(rawValue: "_Path"))
linkAnn.backgroundColor = UIColor.yellow
self.currentPage?.addAnnotation(linkAnn)
self.drawView.removeFromSuperview()
self.closeBtn.isEnabled = false
self.saveBtn.isEnabled = false
self.undoBtn.isEnabled = false
self.pencil.isEnabled = true
}
func allButtons() -> [(PDFAnnotation,PDFPage)]{
var list = [(PDFAnnotation,PDFPage)]()
let checkType = PDFAnnotationSubtype.widget.rawValue.replacingOccurrences(of: "/", with: "");
for page in self.pdfView.visiblePages() {
for ann in page.annotations {
if ann.type! == checkType && ann.widgetFieldType == .button {
list.append((ann, page))
}
}
}
return list
}
func allLinks() -> [PDFAnnotation]{
var list = [PDFAnnotation]()
let checkType = PDFAnnotationSubtype.ink.rawValue.replacingOccurrences(of: "/", with: "");
for page in self.pdfView.visiblePages() {
for ann in page.annotations {
if ann.type! == checkType {
list.append(ann)
}
}
}
return list
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

Resources