UIViewRepresentable wont update my ios chart dataset - ios

I am trying to my data derived from an API into a line chart but I can't seem to get it to work. I am storing the data in an observable object so it takes a few seconds to get it so it won't show up on my graph but when I hardcode data it works I am certain that I am getting the data but it simply won't show up. thanks
struct HomeView: View {
#State var tabIndex:Int = 0
#ObservedObject var homeViewModel = HomeViewModel()
init() {
homeViewModel.getTimelineBy("US")
}
var body: some View {
VStack(alignment: .center) {
TimelineChartView(timelineDataSet: self.$homeViewModel.countryTimeline)
}.frame(height: 500.0)
}
}
struct TimelineChartView: UIViewRepresentable {
#Binding var timelineDataSet: [ChartDataEntry]
func updateUIView(_ uiView: LineChartView, context: UIViewRepresentableContext<TimelineChartView>) {
}
var lineChart = LineChartView()
func makeUIView(context: UIViewRepresentableContext<TimelineChartView>) -> LineChartView {
setUpChart()
return lineChart
}
func setUpChart() {
lineChart.noDataText = "No Data Available"
lineChart.rightAxis.enabled = false
lineChart.backgroundColor = .white
let dataSets = [getLineChartDataSet()]
let yAxis = lineChart.leftAxis
yAxis.labelFont = .boldSystemFont(ofSize: 13)
yAxis.setLabelCount(5, force: false)
yAxis.labelTextColor = .black
yAxis.axisLineColor = .black
yAxis.labelPosition = .outsideChart
lineChart.xAxis.labelPosition = .bottom
lineChart.xAxis.labelFont = .boldSystemFont(ofSize: 13)
lineChart.xAxis.labelTextColor = .black
lineChart.xAxis.axisLineColor = .systemBlue
lineChart.animate(xAxisDuration: 2.5)
lineChart.notifyDataSetChanged()
let data = LineChartData(dataSets: dataSets)
data.setValueFont(.systemFont(ofSize: 7, weight: .black))
lineChart.data = data
}
func getChartDataPoints(selectedTimelineData: [ChartDataEntry]) -> [ChartDataEntry] {
var dataPoints: [ChartDataEntry] = []
for eachTimeline in selectedTimelineData {
let entry = ChartDataEntry(x: eachTimeline.x, y: eachTimeline.y)
dataPoints.append(entry)
}
return dataPoints
}
func getLineChartDataSet() -> LineChartDataSet {
let test = getChartDataPoints(selectedTimelineData: timelineDataSet)
let set = LineChartDataSet(entries: test, label: "DataSet")
set.lineWidth = 4
set.drawCirclesEnabled = false
set.mode = .cubicBezier
set.fillAlpha = 0.9
set.drawFilledEnabled = true
set.highlightColor = .systemRed
return set
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
HomeView()
}
}

Let me share some of my codes working. When the binding object is changed the chart will show the data changed.
struct HomeView: View {
#State var barData: [String: Double] = [String: Double]()
var body: some View {
NavigationView {
List {
CustomeView([BarChartVM(arg: self.$barData, title: "BarChart")])
}
}
}
}
struct CustomeView<Page:View>: View {
var viewControllers: [UIHostingController<Page>]
init(_ views: [Page]) {
self.viewControllers = views.map { UIHostingController(rootView: $0) }
}
var body: some View {
CustomeViewController(controllers: viewControllers)
}
}
struct CustomeViewController: UIViewControllerRepresentable {
var controllers: [UIViewController]
func makeUIViewController(context: Context) -> UIPageViewController {
let pageViewController = UIPageViewController(
transitionStyle: .scroll,
navigationOrientation: .horizontal)
return pageViewController
}
func updateUIViewController(_ pageViewController: UIPageViewController, context: Context) {
pageViewController.setViewControllers(
[controllers[0]], direction: .forward, animated: true)
}
}
struct BarChartVM: UIViewRepresentable {
#Binding var arg: [String: Double]
var title: String = ""
let chart = BarChartView()
func makeUIView(context: UIViewRepresentableContext<BarChartVM>) -> BarChartView {
setUpChart()
return chart
}
func updateUIView(_ uiView: BarChartView, context: UIViewRepresentableContext<BarChartVM>) {
updateChartData()
}
func setUpChart() {
chart.noDataText = "No data available"
let pointArray = arg.sorted(by: <).map { $0.key }
}
func updateChartData() {
var entries = [BarChartDataEntry]()
let valueArray = arg.sorted(by: <).map { $1 }
for i in 0..<valueArray.count {
let entry = BarChartDataEntry(x: Double(i), yValues: [valueArray[i]])
entries.append( entry)
}
let set = BarChartDataSet(entries: entries, label: title)
set.colors = ChartColorTemplates.material()
let data = BarChartData(dataSet: set)
chart.data = data
}
}

Related

OTC View autolayout in SwiftUI

I'm new to swiftui and swift basically, i made One-time-code screen, and here i have a problem. When i run project on my old phone (iphone 6) when keyboard appears, my textfield size changes (it gets very thin vertically). So i was wondering is there any way to add autolayout for older devices?
Here is my code
struct OneTimeCodeBoxes: View {
#Binding var codeDict: [Int: String]
#State var firstResponderIndex = 0
var onCommit: (()->Void)?
var body: some View {
HStack {
ForEach(0..<codeDict.count) { i in
let isEmpty = codeDict[i]?.isEmpty == true
OneTimeCodeInput(
index: i,
codeDict: $codeDict,
firstResponderIndex: $firstResponderIndex,
onCommit: onCommit
)
.aspectRatio(1, contentMode: .fit)
.overlay(RoundedRectangle(cornerRadius: 10)
.stroke(lineWidth: isEmpty ? 1 : 2)
.foregroundColor(isEmpty ? .secondary : .green))
}
}
}
}
struct OneTimeCodeBoxes_Previews: PreviewProvider {
static var previews: some View {
OneTimeCodeBoxes(codeDict: .constant([0: "", 1: "", 2: "", 3: ""]))
.padding()
.previewLayout(.sizeThatFits)
}
}
Here is my OneTimeCodeInput part of code
struct OneTimeCodeInput: UIViewRepresentable {
typealias UIViewType = UITextField
let index: Int
#Binding var codeDict: [Int: String]
#Binding var firstResponderIndex: Int
var onCommit: (()->Void)?
class Coordinator: NSObject, UITextFieldDelegate {
let index: Int
#Binding var codeDict: [Int: String]
#Binding var firstResponderIndex: Int
private lazy var codeDigits: Int = codeDict.count
init(index: Int, codeDict: Binding<[Int: String]>, firstResponderIndex: Binding<Int>) {
self.index = index
self._codeDict = codeDict
self._firstResponderIndex = firstResponderIndex
}
#objc func textFieldEditingChanged(_ textField: UITextField) {
print("textField.text!", textField.text!)
guard textField.text!.count == codeDigits else { return }
codeDict = textField.text!.enumerated().reduce(into: [Int: String](), { dict, tuple in
let (index, char) = tuple
dict.updateValue(String(char), forKey: index)
})
firstResponderIndex = codeDigits - 1
}
func textField(_ textField: UITextField,
shouldChangeCharactersIn range: NSRange,
replacementString string: String) -> Bool
{
print("replacementString", string)
if string.isBackspace {
codeDict.updateValue("", forKey: index)
firstResponderIndex = max(0, textField.text == "" ? index - 1 : index)
return false
}
for i in index..<min(codeDict.count, index + string.count) {
codeDict.updateValue(string.stringAt(index: i - index), forKey: i)
// print(codeDict)
firstResponderIndex = min(codeDict.count - 1, index + string.count)
}
return true
}
}
func makeCoordinator() -> Coordinator {
.init(index: index, codeDict: $codeDict, firstResponderIndex: $firstResponderIndex)
}
func makeUIView(context: Context) -> UITextField {
let tf = BackspaceTextField(onDelete: {
firstResponderIndex = max(0, index - 1)
})
tf.addTarget(context.coordinator, action: #selector(Coordinator.textFieldEditingChanged), for: .editingChanged)
tf.delegate = context.coordinator
tf.keyboardType = .numberPad
tf.textContentType = .oneTimeCode
tf.font = .preferredFont(forTextStyle: .largeTitle)
tf.textAlignment = .center
return tf
}
func updateUIView(_ uiView: UITextField, context: Context) {
uiView.text = codeDict[index]
if index == firstResponderIndex {
uiView.becomeFirstResponder()
}
if index == firstResponderIndex,
codeDict.values.filter({ !$0.isEmpty }).count == codeDict.count
{
onCommit?()
}
}
}
extension OneTimeCodeInput {
class BackspaceTextField: UITextField {
var onDelete: (()->Void)?
init(onDelete: (()->Void)?) {
self.onDelete = onDelete
super.init(frame: .zero)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func deleteBackward() {
super.deleteBackward()
onDelete?()
}
}
}
This is how it shows on Ipnone13 simulator, and it's correct. I'm trying to do same on older devices

swiftUI ScrollView UIRefreshControl overlay on navigation bar

Until iOS 15.2 , the SwiftUI ScrollView doesn't support refreshable{}, so I using UIRefreshControl to solve this issues. Everything went smoothly and success was just one step away ;-(
full code ContentView.swift :
import SwiftUI
extension UIView {
func viewsInHierarchy<ViewType: UIView>() -> [ViewType]? {
var views: [ViewType] = []
viewsInHierarchy(views: &views)
return views.count > 0 ? views : nil
}
fileprivate func viewsInHierarchy<ViewType: UIView>(views: inout [ViewType]) {
subviews.forEach { eachSubView in
if let matchingView = eachSubView as? ViewType {
views.append(matchingView)
}
eachSubView.viewsInHierarchy(views: &views)
}
}
func searchViewAnchestorsFor<ViewType: UIView>(
_ onViewFound: (ViewType) -> Void
) {
if let matchingView = self.superview as? ViewType {
onViewFound(matchingView)
} else {
superview?.searchViewAnchestorsFor(onViewFound)
}
}
func searchViewAnchestorsFor<ViewType: UIView>() -> ViewType? {
if let matchingView = self.superview as? ViewType {
return matchingView
} else {
return superview?.searchViewAnchestorsFor()
}
}
func printViewHierarchyInformation(_ level: Int = 0) {
printViewInformation(level)
self.subviews.forEach { $0.printViewHierarchyInformation(level + 1) }
}
func printViewInformation(_ level: Int) {
let leadingWhitespace = String(repeating: " ", count: level)
let className = "\(Self.self)"
let superclassName = "\(self.superclass!)"
let frame = "\(self.frame)"
let id = (self.accessibilityIdentifier == nil) ? "" : " `\(self.accessibilityIdentifier!)`"
print("\(leadingWhitespace)\(className): \(superclassName)\(id) \(frame)")
}
}
final class ViewControllerResolver: UIViewControllerRepresentable {
let onResolve: (UIViewController) -> Void
init(onResolve: #escaping (UIViewController) -> Void) {
self.onResolve = onResolve
}
func makeUIViewController(context: Context) -> ParentResolverViewController {
ParentResolverViewController(onResolve: onResolve)
}
func updateUIViewController(_ uiViewController: ParentResolverViewController, context: Context) { }
}
class ParentResolverViewController: UIViewController {
let onResolve: (UIViewController) -> Void
init(onResolve: #escaping (UIViewController) -> Void) {
self.onResolve = onResolve
super.init(nibName: nil, bundle: nil)
}
required init?(coder: NSCoder) {
fatalError("Use init(onResolve:) to instantiate ParentResolverViewController.")
}
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
if let parent = parent {
//parent.navigationController?.navigationBar.prefersLargeTitles = true
//parent.navigationItem.largeTitleDisplayMode = .never
self.onResolve(parent)
// print("didMove(toParent: \(parent)")
}
}
}
struct ContentView: View {
var body: some View {
NavigationView {
ScrollView {
VStack {
HStack {
Spacer()
Text("Content")
Spacer()
}
Spacer()
}
.background(Color.red)
}
.navigationTitle("title")
.navigationBarTitleDisplayMode(.inline)
.overlay(
ViewControllerResolver { parent in
var scrollView: UIScrollView?
if let scrollViewsInHierarchy: [UIScrollView] = parent.view.viewsInHierarchy() {
if scrollViewsInHierarchy.count == 1, let firstScrollViewInHierarchy = scrollViewsInHierarchy.first {
scrollView = firstScrollViewInHierarchy
}
}
if let scrollView = scrollView {
guard scrollView.refreshControl == nil else { return }
let refreshControl = UIRefreshControl()
refreshControl.transform = CGAffineTransform(scaleX: 0.75, y: 0.75)
refreshControl.layoutIfNeeded()
let uiAction = UIAction(handler: { uiAction in
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
refreshControl.endRefreshing()
}
})
refreshControl.addAction(uiAction, for: .primaryActionTriggered)
scrollView.refreshControl = refreshControl
}
}
.frame(width: 0, height: 0)
)
}
}
}

How to change View while app is running in SwiftUI

struct AnimationView: UIViewRepresentable {
#Binding var cnt: Int
var imageView: UIImageView = UIImageView(image: effctPicker(cnt: 0))
func makeUIView(context: Self.Context) -> UIImageView {
return imageView
}
func updateUIView(_ uiView: UIImageView, context: UIViewRepresentableContext<AnimationView>) {
if self.cnt == 0{
self.imageView.image = effctPicker(cnt: 0)
}
else {
self.imageView.image = effctPicker(cnt: self.cnt)
}
}
}
struct ContentView: View {
#State var count:Int = 0
#State var audioPlayer: AVAudioPlayer!
#State var bg: String = "bg"
var body: some View {
Button(action:{
count += 1
switch count
{
case 1:
self.bg = "bg_2"
default:
count = 0
self.bg = "bg"
}
}, label: {
ZStack {
Image(bg)
.resizable()
AnimationView(cnt: $count)
}.onAppear {
let sound = Bundle.main.path(forResource: "freelove", ofType: "mp3")
self.audioPlayer = try! AVAudioPlayer(contentsOf: URL(fileURLWithPath: sound!))
self.audioPlayer.play()
}
}
)
}
}
var springImages = [UIImage(named: "eft1")!, UIImage(named: "eft2")!,UIImage(named: "eft3")!, UIImage(named: "eft4")!]
var rainImages = [UIImage(named: "raineft_1")!, UIImage(named: "raineft_2")!,UIImage(named: "raineft_3")!]
func effctPicker(cnt : Int) ->UIImage{
if cnt == 0{
return UIImage.animatedImage(with: springImages, duration: 0.4)!
}
else {
return UIImage.animatedImage(with: rainImages, duration: 0.4)!
}
}
First of all, I'm sorry that my english skill is not good. And this is my first question in stackoverflow
This is my animation view
I want when i press the button, binded "cnt" will change and updaateUIView will be called.
But it didn't worked. Should I add coordinator? If so, what should I do?

MapKit Tile Overlay takes a long time to load

I am trying to build an app which loops through overlay tiles. The problem is the map tiles take forever to reload when the map displays. What is the best way around this issue? I'm not sure if it is a caching issue which I think MapKit does itself. My guess is it is a Swift redrawing issue. My code is below, and I appreciate your help.
// Copyright 2020 Oklahoma Weather Blog
//
import SwiftUI
import MapKit
import SwiftSoup
/*
struct RadarMapView: UIViewRepresentable {
var coordinate: CLLocationCoordinate2D
var tileRenderer = MKOverlayRenderer()
func mapView(mapView: MKMapView, rendererForOverlay overlay: MKOverlay) -> MKOverlayRenderer {
guard let tileOverlay = overlay as? MKTileOverlay else {
return MKOverlayRenderer()
}
return MKTileOverlayRenderer(tileOverlay: tileOverlay)
}
func makeUIView(context: Context) -> MKMapView {
var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
template = "https://tilecache.rainviewer.com/v2/ radar/1600575600/512/{z}/{x}/{y}/6/0_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlay.canReplaceMapContent = false
let mapView = MKMapView(frame: .zero)
var renderedOverlay = MKTileOverlayRenderer(tileOverlay: overlay)
mapView.addOverlay(overlay, level: .aboveLabels)
mapView.setNeedsDisplay()
return mapView
}
func updateUIView(_ view: MKMapView, context: Context) {
let span = MKCoordinateSpan(latitudeDelta: 0.3, longitudeDelta: 0.3)
let region = MKCoordinateRegion(center: coordinate, span: span)
view.setRegion(region, animated: true)
}
}
*/
func getTimesURL() -> [String] {
let myURLString = "https://api.rainviewer.com/public/maps.json"
guard let myURL = URL(string: myURLString) else {
printToConsole("Error: \(myURLString) doesn't seem to be a valid URL")
return []
}
do {
let myHTMLString = try String(contentsOf: myURL)
do {
let doc: Document = try SwiftSoup.parse(myHTMLString)
let text = try doc.text()
let resultArray = text.trimmingCharacters(in: CharacterSet(charactersIn: "[]"))
.components(separatedBy:",")
return resultArray
} catch Exception.Error( _, let message) {
printToConsole(message)
} catch {
printToConsole("error")
}
} catch let error as NSError {
printToConsole("Error: \(error)")
}
return []
}
func getOverlays() -> [MKTileOverlay] {
var overlays: [MKTileOverlay] = []
for time in getTimesURL() {
let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/256/{z}/{x}/{y}/7/1_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlays.append(overlay)
}
return overlays
}
struct RadarView: View {
private static let mapStyles: [MKMapType] = [.hybrid, .hybridFlyover, .mutedStandard, .satellite, .satelliteFlyover, .standard]
#State var mapTime = "1600585200"
let cache = NSCache<NSString, MKTileOverlay>()
#AppStorage("selectedMapStyle") var selectedMapStyle = 0
#State private var showingSheet = false
private static var overlayArray: [MKTileOverlay] {
getOverlays()
}
private static var timeArray: [String] {
getTimesURL()
}
func dateToString(_ epoch: String) -> String{
let dateFormatterPrint = DateFormatter()
dateFormatterPrint.dateFormat = "MM/dd hh:mm a"
let date = Date(timeIntervalSince1970: TimeInterval(Int(epoch)!))
return dateFormatterPrint.string(from: date)
}
#State private var timeIndex: Double = 0
#State private var loopMap: Bool = false
#State var radarTimer: Timer?
var body: some View {
VStack{
ZStack(alignment: .top) {
RadarMapView(mapStyle: RadarView.mapStyles[selectedMapStyle], overlay: RadarView.overlayArray[Int(timeIndex)]).edgesIgnoringSafeArea(.bottom)
HStack{
VStack{
Slider(value: $timeIndex.animation(.linear), in: 0...9, step: 1)
Text("\(dateToString(RadarView.timeArray[Int(timeIndex)]))")
}
Button(action: {
loopMap.toggle()
if loopMap {
radarTimer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
// do something here
if RadarView.overlayArray.count > 0 {
withAnimation{
timeIndex = Double((Int(timeIndex) + 1) % RadarView.overlayArray.count )
}
}
}
} else {
radarTimer?.invalidate()
}
}, label: {
if !loopMap { Text("Loop") }
else { Text("Pause") }
})
}.padding(.horizontal).padding(.horizontal).background(Color.init(UIColor.systemBackground).opacity(0.75))
}
HStack(){
Spacer()
Button(action: {
/*
selectedMapStyle = (selectedMapStyle + 1) % mapStyles.count
*/
showingSheet.toggle()
}, label: {
Image(systemName: "map.fill").resizable()
.renderingMode(.template)
.font(.title)
.foregroundColor(Color.primary)
.frame(width: 20, height: 20)
}).padding()
}.padding(.horizontal).padding(.bottom).background(Color.init(UIColor.systemBackground).opacity(0.75))
}.actionSheet(isPresented: $showingSheet) {
ActionSheet(title: Text("What map style do you want?"), message: Text("Please select one option below"), buttons: [
.default(Text("Muted")) { self.selectedMapStyle = 2 },
.default(Text("Satellite")) { self.selectedMapStyle = 3 },
.default(Text("Satellite w/ Roads")) { self.selectedMapStyle = 0 },
.default(Text("Satellite 3-D")) { self.selectedMapStyle = 4 },
.default(Text("3-D Satellite w/ Roads")) { self.selectedMapStyle = 1 },
.default(Text("Standard")) { self.selectedMapStyle = 5 },
.cancel(Text("Dismiss"))
])
}.edgesIgnoringSafeArea(.bottom).navigationBarTitle("Radar")
}
}
struct RadarMapView: UIViewRepresentable {
var mapStyle: MKMapType
var overlay: MKTileOverlay
class Coordinator: NSObject, MKMapViewDelegate {
var parent: RadarMapView
init(_ parent: RadarMapView) {
self.parent = parent
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let renderer = MKTileOverlayRenderer(overlay: overlay)
return renderer
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIView(context: Context) -> MKMapView {
return MKMapView()
}
func updateUIView(_ mapView: MKMapView, context: Context) {
// var template = "https://tile.openstreetmap.org/{z}/{x}/{y}.png"
//1600626600
mapView.mapType = self.mapStyle
mapView.delegate = context.coordinator
let overlays = mapView.overlays
mapView.addOverlay(overlay)
let regionRadius: CLLocationDistance = 50000
let location = CLLocationCoordinate2D(latitude: 33.7490, longitude: -84.3880)
let coordinateRegion = MKCoordinateRegion(center: location,
latitudinalMeters: regionRadius * 2.0, longitudinalMeters: regionRadius * 2.0)
mapView.setRegion(coordinateRegion, animated: true)
for overlay in overlays {
// remove all MKPolyline-Overlays
if overlay is MKTileOverlay {
mapView.removeOverlay(overlay)
}
}
}
}
struct RadarView_Previews: PreviewProvider {
static var previews: some View {
RadarView()
}
}
I had a similar problem a few days ago (with a different tile server) and solved it by using 512px tiles.
My theory is, that almost nobody uses 256px tiles, so servers don't cache them.
Do something like
func getOverlays() -> [MKTileOverlay] {
var overlays: [MKTileOverlay] = []
for time in getTimesURL() {
let template = "https://tilecache.rainviewer.com/v2/radar/\(time)/512/{z}/{x}/{y}/7/1_1.png"
let overlay = MKTileOverlay(urlTemplate:template)
overlay.tileSize = CGSize(width: 512, height: 512)
overlays.append(overlay)
}
return overlays
}

PDFView with PKCanvasView drawingGestureRecognizer on iOS14

Minimum code to add a PKCanvasView to PDFView. The PKCanvasView displays properly if the PKDrawing is set. However on iOS14, the drawingGestureRecognizer does not fire. Works on iOS13
import UIKit
import PDFKit
import PencilKit
class ViewController: UIViewController {
#IBOutlet var pdfView: PDFView!
var scrollView : UIScrollView!
var pkView : PKCanvasView!
var docView : UIView!
var drawing : PKDrawing!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
setupPDF()
setupPencil()
}
func setupPDF() {
let fileURL = Bundle.main.url(forResource: "test", withExtension: "pdf")!
let pdfDocument = PDFDocument(url: fileURL)
pdfView.document = pdfDocument
}
func setupPencil() {
for scroll in self.allSubViews(in: self.pdfView, ofType: UIScrollView.self) {
self.scrollView = scroll
break
}
for view in self.allSubViews(in: self.scrollView, ofType: UIView.self) {
if "\(view)".starts(with: "<PDFDocumentView: ") {
self.docView = view
break
}
}
self.pkView = PKCanvasView(frame: CGRect(origin: CGPoint.zero, size: self.docView.bounds.size))
self.pkView.isOpaque = false
self.pkView.backgroundColor = .clear
self.docView.addSubview(self.pkView)
self.pkView.tool = PKInkingTool(.pen, color: .black, width:0.4)
self.pkView.overrideUserInterfaceStyle = .light
if let _drawing = self.drawing {
self.pkView.drawing = _drawing
}
self.scrollView.panGestureRecognizer.minimumNumberOfTouches = 2
self.scrollView.addGestureRecognizer(self.pkView.drawingGestureRecognizer)
}
func allSubViews<T: UIView>(in view:UIView, ofType type: T.Type) -> [T] {
var all: [T] = []
func getSubview(view: UIView) {
if let aView = view as? T {
all.append(aView)
}
guard view.subviews.count > 0 else { return }
view.subviews.forEach{ getSubview(view: $0) }
}
getSubview(view: view)
return all
}
}
FYI, the pages are rendered dynamically and can be added and removed from the hierarchy. If you're using this code, you need to bringSubviewToFront when pages change.

Resources