How to change View while app is running in SwiftUI - ios

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?

Related

dropdown array becomes empty for few cells after adding cells in tableview and scroll up

Initially when I tap of hierarchy button and parentLocationLevelTwoBtnAction at that time parentLocationNameArr is not empty but after adding few more cells and I scroll up parentLocationNameArr is empty
import UIKit
import DropDown
import ObjectMapper
//Stock Btn Cell Delegate
protocol AddStorageLocationMasterBtnCellDelegate : AnyObject {
func didPressButton(_ tag: Int)
func didPressHierachyButton(_ tag: Int)
func didPressHierachytext(_ tag: Int,sender:String)
func didPressParenttext(_ tag: Int,sender:String, id:String)
func didPressParentTwotext(_ tag: Int,sender:String, id:String)
}
class AddStorageLocationMasterTVCell: UITableViewCell {
#IBOutlet var parentLTextLHeight: NSLayoutConstraint!
#IBOutlet weak var bgView: UIView!
#IBOutlet weak var deleteBtn: UIButton!
#IBOutlet weak var storageLocationNameView: UIView!
#IBOutlet var warnDescriptionbtn: UIButton!
#IBOutlet weak var storageLocationNameTF: UITextField!
#IBOutlet var warnNamebtn: UIButton!
#IBOutlet weak var descriptionView: UIView!
#IBOutlet weak var descriptionTextView: UITextView!
#IBOutlet weak var hierachyView: UIView!
#IBOutlet weak var hierachyTF: UITextField!
#IBOutlet weak var hierachyBtn: UIButton!
#IBOutlet weak var parentLocationView: UIView!
#IBOutlet weak var parentLocationTF: UITextField!
#IBOutlet weak var parentBgView: UIView!
#IBOutlet weak var parentHeightConstant: NSLayoutConstraint!
#IBOutlet weak var parentLocationLevelOneBtn: UIButton!
#IBOutlet weak var parentLocationLevelTwoView: UIView!
#IBOutlet weak var parentLocationLevelTwoTF: UITextField!
#IBOutlet weak var parentLocationTextLabel: UILabel!
#IBOutlet weak var stackView: UIStackView!
#IBOutlet weak var stackViewHeight: NSLayoutConstraint!
#IBOutlet weak var parentLcationLabel: UILabel!
var cellDelegate: AddStorageLocationMasterBtnCellDelegate?
let dropDown = DropDown() //2
var selectedId = 0
var accountID = ""
var storageLocationArr = NSMutableArray()
var storageLocationLevelThreeArr = NSMutableArray()
var serviceVC = ServiceController()
var parentLocationDict = [String]()
var parentLocationId = [String]()
var parentLocationArr = [NSArray]()
var parentLocationNameArr = [String]()
var parentLocationNameIdsArr = [String]()
var parentLocationDetailsDict = NSMutableArray()
var parentLocationNameLevethreeArr = [String]()
var parentLocationNameLevethreeIdsArr = [String]()
var parentLocationDetailsLevethreeDict = NSMutableArray()
var levelValue = 0
var idLevelThree = ""
override func awakeFromNib() {
super.awakeFromNib()
// Initialization code
self.parentBgView.isHidden = true
self.parentHeightConstant.constant = 0
storageLocationNameView.layer.borderColor = UIColor.gray.cgColor
storageLocationNameView.layer.borderWidth = 0.5
storageLocationNameView.layer.cornerRadius = 3
storageLocationNameView.clipsToBounds = true
descriptionView.layer.borderColor = UIColor.gray.cgColor
descriptionView.layer.borderWidth = 0.5
descriptionView.layer.cornerRadius = 3
descriptionView.clipsToBounds = true
hierachyView.layer.borderColor = UIColor.gray.cgColor
hierachyView.layer.borderWidth = 0.5
hierachyView.layer.cornerRadius = 3
hierachyView.clipsToBounds = true
parentLocationView.layer.borderColor = UIColor.gray.cgColor
parentLocationView.layer.borderWidth = 0.5
parentLocationView.layer.cornerRadius = 3
parentLocationView.clipsToBounds = true
parentLocationLevelTwoView.layer.borderColor = UIColor.gray.cgColor
parentLocationLevelTwoView.layer.borderWidth = 0.5
parentLocationLevelTwoView.layer.cornerRadius = 3
parentLocationLevelTwoView.clipsToBounds = true
bgView.backgroundColor = .white
bgView.layer.cornerRadius = 5.0
bgView.layer.shadowColor = #colorLiteral(red: 0.8039215803, green: 0.8039215803, blue: 0.8039215803, alpha: 1)
bgView.layer.shadowOffset = CGSize(width: 0.0, height: 0.0)
bgView.layer.shadowRadius = 6.0
bgView.layer.shadowOpacity = 0.7
hierachyTF.text = "Level 1"
selectedId = 1
}
#IBOutlet var parentButton: UIButton!
// override func setSelected(_ selected: Bool, animated: Bool) {
// super.setSelected(selected, animated: animated)
//
// // Configure the view for the selected state
// }
#IBAction func deleteBtnAction(_ sender: UIButton) {
cellDelegate?.didPressButton(sender.tag)
}
var tagVal=Int()
#IBAction func hierachyBtnAction(_ sender: UIButton) {
let selectedTagHierachy = sender.tag
cellDelegate?.didPressHierachyButton(selectedTagHierachy)
dropDown.dataSource = ["Level 1","Level 2","Level 3"]//4
dropDown.anchorView = sender //5
dropDown.bottomOffset = CGPoint(x: 0, y: sender.frame.size.height) //6
dropDown.show() //7
dropDown.selectionAction = { (index: Int, item: String) in //8
// guard let _ = self else { return }
// sender.setTitle(item, for: .normal) //9
self.selectedId = index
self.hierachyTF.text = item
self.cellDelegate?.didPressHierachytext(selectedTagHierachy, sender: item)
print(self.levelValue)
if index == 0 {
self.parentBgView.isHidden = true
self.parentHeightConstant.constant = 0
}
else if index == 1{
self.levelValue = index+1
self.get_StorageLocationMaster_API_Call(level: index+1, tag: selectedTagHierachy)
self.parentBgView.isHidden = false
self.parentHeightConstant.constant = 120
self.parentLcationLabel.text = "Parent Location L1"
self.parentLocationLevelTwoView.isHidden = true
}
else if index == 2{
self.levelValue = index+1
self.get_StorageLocationMaster_API_Call(level: index+1, tag: selectedTagHierachy)
self.parentBgView.isHidden = false
self.parentHeightConstant.constant = 120
self.parentLcationLabel.text = "Parent Location L2 and L1"
self.parentLocationLevelTwoView.isHidden = false
}
}
}
#IBAction func parentLocationBtnAction(_ sender: UIButton) {
let selectedTagParentLocation = sender.tag
// cellDelegate?.didPressParenttext(<#T##tag: Int##Int#>, sender: sender, id: <#T##String#>)
dropDown.dataSource = parentLocationNameLevethreeArr//4
dropDown.anchorView = sender //5
dropDown.bottomOffset = CGPoint(x: 0, y: sender.frame.size.height) //6
dropDown.show() //7
dropDown.selectionAction = { (index: Int, item: String) in //8
// guard let _ = self else { return }
// sender.setTitle(item, for: .normal) //9
self.parentLocationLevelTwoTF.text = item
self.cellDelegate?.didPressParentTwotext(selectedTagParentLocation, sender: item, id:self.parentLocationNameLevethreeIdsArr[index] )
}
}
#IBAction func parentLocationLevelTwoBtnAction(_ sender: UIButton)
{
let selectedTagParentLocation = sender.tag
dropDown.dataSource = parentLocationNameArr//4
dropDown.anchorView = sender //5
dropDown.bottomOffset = CGPoint(x: 0, y: sender.frame.size.height) //6
dropDown.show() //7
dropDown.selectionAction = { (index: Int, item: String) in //8
// guard let _ = self else { return }
// sender.setTitle(item, for: .normal) //9
// if self?.parentLocationNameLevethreeArr.count ?? 0>0
// {
// self?.parentLocationLevelTwoTF.text = self?.parentLocationNameLevethreeArr[0]
// self?.cellDelegate?.didPressParentTwotext(sender.tag, sender: self?.parentLocationNameLevethreeArr[0] ?? "", id: self?.parentLocationNameLevethreeIdsArr[0] ?? "")
// }
self.parentLocationTF.text = item
self.idLevelThree = self.parentLocationNameIdsArr[index]
self.cellDelegate?.didPressParenttext(selectedTagParentLocation, sender: item, id: self.parentLocationNameIdsArr[index] )
print(self.levelValue)
if self.levelValue==3
{
self.get_StorageLocationByParentLocation_API_Call(level: 2, tag: selectedTagParentLocation)
}
}
}
// MARK: Get Storage Location Master API Call
func get_StorageLocationByParentLocation_API_Call(level:Int,tag:Int) {
let defaults = UserDefaults.standard
accountID = (defaults.string(forKey: "accountId") ?? "")
// let newString = parentLocation.replacingOccurrences(of: " ", with: "%20")
if idLevelThree != ""
{
let urlStr = Constants.BaseUrl + getAllStorageLocationByParentUrl + "\(2)/" + "\(idLevelThree)/" + accountID
serviceVC.requestGETURL(strURL: urlStr, success: {(result) in
let respVo:GetStorageLocationMasterRespVo = Mapper().map(JSON: result as! [String : Any])!
DispatchQueue.main.async { [self] in
let status = respVo.STATUS_CODE
let message = respVo.STATUS_MSG
if status == 200 {
if message == "SUCCESS" {
if respVo.result != nil {
if respVo.result!.count > 0 {
let resultObj:[String:Any]=result as? [String:Any] ?? [String:Any]()
let resultArr:NSMutableArray=resultObj["result"]as? NSMutableArray ?? NSMutableArray()
self.storageLocationLevelThreeArr = NSMutableArray()
self.storageLocationLevelThreeArr = resultArr
self.parentLocationNameLevethreeArr.removeAll()
self.parentLocationNameLevethreeIdsArr.removeAll()
for obj in self.storageLocationLevelThreeArr {
let sDict=obj as? NSDictionary
let isCanEd:Bool = sDict?.value(forKey: "canEdit") as? Bool ?? false
// sDict(sDict?[i] as AnyObject).value(forKey: "canEdit") as? Bool
if isCanEd == true {
let slocName = sDict?.value(forKey: "slocName") as? String ?? ""
let idsStr = sDict?.value(forKey: "_id") as? String ?? ""
self.parentLocationNameLevethreeIdsArr.append(idsStr)
self.parentLocationNameLevethreeArr.append(slocName)
print("level 2 : \(parentLocationNameLevethreeArr)")
}
}
if self.parentLocationNameLevethreeArr.count > 0 {
self.parentLocationLevelTwoTF.text = self.parentLocationNameLevethreeArr[0]
self.cellDelegate?.didPressParentTwotext(tag, sender: self.parentLocationNameLevethreeArr[0], id: self.parentLocationNameLevethreeIdsArr[0])
}
// else{
// self.parentLocationLevelTwoTF.text = ""
// }
self.parentLTextLHeight.constant=0
self.parentLocationTextLabel.isHidden=true
self.parentLocationTextLabel.text=""
}
else {
self.parentLTextLHeight.constant=40
self.parentLocationTextLabel.isHidden=false
self.parentLocationTextLabel.text="No parent storage location is created currently. Pls create a parent storage location before creating the storage location when use creates a storage location without a custom parent storage location"
self.parentLocationTextLabel.numberOfLines = 0
self.parentLocationTextLabel.lineBreakMode = .byWordWrapping
self.parentLocationLevelTwoTF.text = ""
self.parentLocationNameLevethreeArr=[String]()
self.parentLocationNameLevethreeIdsArr=[String]()
}
}
}
}
else {
// self.view.makeToast(message)
}
}
}) { (error) in
// self.view.makeToast("app.SomethingWentToWrongAlert".localize())
print("Oops, your connection seems off... Please try again later")
}
}
else
{
self.parentLTextLHeight.constant=40
self.parentLocationTextLabel.isHidden=false
self.parentLocationTextLabel.text="No level 2 storage location exists currently. Pls create a level 2 storage location before creating levele 3 storage location"
}
}
// MARK: Get AddressBook API Call
func get_StorageLocationMaster_API_Call(level:Int,tag:Int) {
let defaults = UserDefaults.standard
accountID = (defaults.string(forKey: "accountId") ?? "")
let urlStr = Constants.BaseUrl + getAllStorageLocationByHierachyLevelUrl + "\(1)/" + accountID
serviceVC.requestGETURL(strURL: urlStr, success: {(result) in
let respVo:GetStorageLocationMasterRespVo = Mapper().map(JSON: result as! [String : Any])!
DispatchQueue.main.async {
let status = respVo.STATUS_CODE
let message = respVo.STATUS_MSG
if status == 200 {
if message == "SUCCESS" {
if respVo.result != nil {
if respVo.result!.count > 0 {
let resultObj:[String:Any]=result as? [String:Any] ?? [String:Any]()
let resultArr:NSMutableArray=resultObj["result"]as? NSMutableArray ?? NSMutableArray()
print(resultArr)
self.storageLocationArr = NSMutableArray()
self.storageLocationArr = resultArr
if self.storageLocationArr.count==0
{
self.parentLTextLHeight.constant=40
self.parentLocationTextLabel.isHidden=false
}
else
{
self.parentLTextLHeight.constant=0
self.parentLocationTextLabel.isHidden=true
let ddict=self.storageLocationArr[0] as? NSDictionary
self.parentLocationNameArr = [String]()
self.parentLocationNameIdsArr = [String]()
for obj in self.storageLocationArr {
let sDict=obj as? NSDictionary
let isCanEd:Bool = sDict?.value(forKey: "canEdit") as? Bool ?? false
// sDict(sDict?[i] as AnyObject).value(forKey: "canEdit") as? Bool
if isCanEd == true {
let slocName = sDict?.value(forKey: "slocName") as? String ?? ""
let idsStr = sDict?.value(forKey: "_id") as? String ?? ""
self.parentLocationNameIdsArr.append(idsStr)
self.parentLocationNameArr.append(slocName)
print(self.parentLocationNameArr)
self.parentLocationDetailsDict.addObjects(from: sDict?.value(forKey: "parentLocationDetails") as? NSMutableArray as! [Any])
}
}
let parentLocationDetails=ddict?.value(forKey: "parentLocationDetails")as? NSArray
print(self.levelValue)
if self.levelValue==3
{
if self.parentLocationNameArr.count>0
{
self.idLevelThree = self.parentLocationNameIdsArr[0] ?? ""
self.parentLocationTF.text = self.parentLocationNameArr[0]
self.cellDelegate?.didPressParenttext(tag, sender: self.parentLocationNameArr[0], id: self.parentLocationNameIdsArr[0])
}
else{
self.parentLTextLHeight.constant=40
self.parentLocationTextLabel.isHidden=false
self.parentLocationTextLabel.text="No level 2 storage location exists currently. Pls create a level 2 storage location before creating levele 3 storage location"
}
// if self.parentLocationDetailsDict.count > 0
// {
self.get_StorageLocationByParentLocation_API_Call(level: 2, tag:tag)
print("parent level 2 :\(self.parentLocationNameLevethreeArr)")
// }
}
else
{
if self.parentLocationNameArr.count>0
{
self.parentLocationTF.text = self.parentLocationNameArr[0]
self.cellDelegate?.didPressParenttext(tag, sender: self.parentLocationNameArr[0], id: self.parentLocationNameIdsArr[0])
}
else{
self.parentLTextLHeight.constant=40
self.parentLocationTextLabel.isHidden=false
self.parentLocationTextLabel.text="No level 1 storage location exists currently. Pls create a level 1 storage location before creating levele 3 storage location"
}
}
}
}
else {
}
}
}
}
else {
// self.view.makeToast(message)
}
}
}) { (error) in
// self.view.makeToast("app.SomethingWentToWrongAlert".localize())
print("Oops, your connection seems off... Please try again later")
}
}
}
Thanks in advance🙏

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.

UIViewRepresentable wont update my ios chart dataset

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
}
}

SwiftUI Custom Camera View?

I'm playing with SwiftUI and trying to build a custom camera with it. I found tutorials on how to use system built-in camera with SwiftUI(using ImagePickerController) and how to build a custom camera with storyboard.
I've already built a struct CameraViewController: UIViewControllerRepresentable that initialize the camera and setup capturesession.(using AVFoundation).
First I'm not sure how to setup func makeUIViewController for CameraViewController struct, since I dont know which controller class to conform to.
Also I don't know how to integrate my CameraViewController class into the app with SwiftUI. Can someone help?
Thanks!
SwiftUI - Custom Camera Implementation Example
CustomCameraPhotoView / Main Screen - Photo Preview
2. CustomCameraView / Camera Screen - Combines SwiftUI View (Record Button) with UIKit ViewController
3. CustomCameraRepresentable / Custom Camera ViewController SwiftUI Wrapper
4. CustomCameraController / Custom Camera View Controller
5. CaptureButtonView / SwiftUI View - Capture Button
Note: Avoid app crashing by adding this Privacy - Camera Usage Description into the Info.plist file.
import SwiftUI
import AVFoundation
struct CustomCameraPhotoView: View {
#State private var image: Image?
#State private var showingCustomCamera = false
#State private var inputImage: UIImage?
var body: some View {
NavigationView {
VStack {
ZStack {
Rectangle().fill(Color.secondary)
if image != nil
{
image?
.resizable()
.aspectRatio(contentMode: .fill)
}
else
{
Text("Take Photo").foregroundColor(.white).font(.headline)
}
}
.onTapGesture {
self.showingCustomCamera = true
}
}
.sheet(isPresented: $showingCustomCamera, onDismiss: loadImage) {
CustomCameraView(image: self.$inputImage)
}
.edgesIgnoringSafeArea(.all)
}
}
func loadImage() {
guard let inputImage = inputImage else { return }
image = Image(uiImage: inputImage)
}
}
struct CustomCameraView: View {
#Binding var image: UIImage?
#State var didTapCapture: Bool = false
var body: some View {
ZStack(alignment: .bottom) {
CustomCameraRepresentable(image: self.$image, didTapCapture: $didTapCapture)
CaptureButtonView().onTapGesture {
self.didTapCapture = true
}
}
}
}
struct CustomCameraRepresentable: UIViewControllerRepresentable {
#Environment(\.presentationMode) var presentationMode
#Binding var image: UIImage?
#Binding var didTapCapture: Bool
func makeUIViewController(context: Context) -> CustomCameraController {
let controller = CustomCameraController()
controller.delegate = context.coordinator
return controller
}
func updateUIViewController(_ cameraViewController: CustomCameraController, context: Context) {
if(self.didTapCapture) {
cameraViewController.didTapRecord()
}
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, UINavigationControllerDelegate, AVCapturePhotoCaptureDelegate {
let parent: CustomCameraRepresentable
init(_ parent: CustomCameraRepresentable) {
self.parent = parent
}
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
parent.didTapCapture = false
if let imageData = photo.fileDataRepresentation() {
parent.image = UIImage(data: imageData)
}
parent.presentationMode.wrappedValue.dismiss()
}
}
}
class CustomCameraController: UIViewController {
var image: UIImage?
var captureSession = AVCaptureSession()
var backCamera: AVCaptureDevice?
var frontCamera: AVCaptureDevice?
var currentCamera: AVCaptureDevice?
var photoOutput: AVCapturePhotoOutput?
var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
//DELEGATE
var delegate: AVCapturePhotoCaptureDelegate?
func didTapRecord() {
let settings = AVCapturePhotoSettings()
photoOutput?.capturePhoto(with: settings, delegate: delegate!)
}
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func setup() {
setupCaptureSession()
setupDevice()
setupInputOutput()
setupPreviewLayer()
startRunningCaptureSession()
}
func setupCaptureSession() {
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
func setupDevice() {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(deviceTypes: [AVCaptureDevice.DeviceType.builtInWideAngleCamera],
mediaType: AVMediaType.video,
position: AVCaptureDevice.Position.unspecified)
for device in deviceDiscoverySession.devices {
switch device.position {
case AVCaptureDevice.Position.front:
self.frontCamera = device
case AVCaptureDevice.Position.back:
self.backCamera = device
default:
break
}
}
self.currentCamera = self.backCamera
}
func setupInputOutput() {
do {
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera!)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray([AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.jpeg])], completionHandler: nil)
captureSession.addOutput(photoOutput!)
} catch {
print(error)
}
}
func setupPreviewLayer()
{
self.cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
self.cameraPreviewLayer?.videoGravity = AVLayerVideoGravity.resizeAspectFill
self.cameraPreviewLayer?.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
self.cameraPreviewLayer?.frame = self.view.frame
self.view.layer.insertSublayer(cameraPreviewLayer!, at: 0)
}
func startRunningCaptureSession(){
captureSession.startRunning()
}
}
struct CaptureButtonView: View {
#State private var animationAmount: CGFloat = 1
var body: some View {
Image(systemName: "video").font(.largeTitle)
.padding(30)
.background(Color.red)
.foregroundColor(.white)
.clipShape(Circle())
.overlay(
Circle()
.stroke(Color.red)
.scaleEffect(animationAmount)
.opacity(Double(2 - animationAmount))
.animation(Animation.easeOut(duration: 1)
.repeatForever(autoreverses: false))
)
.onAppear
{
self.animationAmount = 2
}
}
}
Here's a version, where you can pass any frame size for camera preview layer.
If you have a back button, ozmpai answer does not work out of the box. I have edited ozmpai answer, so all kudos still goes to him.
Don't like the shared singleton, but for now, haven't figured a better approach for adaptation of SwiftUI view lifecycle yet. As SwiftUI is probably using black magic behind it.
Also, passing a bool to take a photo is probably not the greatest approach, so I have refactored it with a closure.
import SwiftUI
struct MyCameraView: View {
#State private var image: UIImage?
var customCameraRepresentable = CustomCameraRepresentable(
cameraFrame: .zero,
imageCompletion: { _ in }
)
var body: some View {
CustomCameraView(
customCameraRepresentable: customCameraRepresentable,
imageCompletion: { newImage in
self.image = newImage
}
)
.onAppear {
customCameraRepresentable.startRunningCaptureSession()
}
.onDisappear {
customCameraRepresentable.stopRunningCaptureSession()
}
if let image = image {
Image(uiImage: image)
.resizable()
.aspectRatio(contentMode: .fit)
}
}
}
import SwiftUI
struct CustomCameraView: View {
var customCameraRepresentable: CustomCameraRepresentable
var imageCompletion: ((UIImage) -> Void)
var body: some View {
GeometryReader { geometry in
VStack {
let frame = CGRect(x: 0, y: 0, width: geometry.size.width, height: geometry.size.height - 100)
cameraView(frame: frame)
HStack {
CameraControlsView(captureButtonAction: { [weak customCameraRepresentable] in
customCameraRepresentable?.takePhoto()
})
}
}
}
}
private func cameraView(frame: CGRect) -> CustomCameraRepresentable {
customCameraRepresentable.cameraFrame = frame
customCameraRepresentable.imageCompletion = imageCompletion
return customCameraRepresentable
}
}
import SwiftUI
struct CameraControlsView: View {
var captureButtonAction: (() -> Void)
var body: some View {
CaptureButtonView()
.onTapGesture {
captureButtonAction()
}
}
}
import SwiftUI
struct CaptureButtonView: View {
#Environment(\.colorScheme) var colorScheme
#State private var animationAmount: CGFloat = 1
var body: some View {
Image(systemName: "camera")
.font(.largeTitle)
.padding(20)
.background(colorScheme == .dark ? Color.white : Color.black)
.foregroundColor(colorScheme == .dark ? Color.black : Color.white)
.clipShape(Circle())
.overlay(
Circle()
.stroke(colorScheme == .dark ? Color.white : Color.black)
.scaleEffect(animationAmount)
.opacity(Double(2 - animationAmount))
.animation(
Animation.easeOut(duration: 1)
.repeatForever(autoreverses: false)
)
)
.onAppear {
animationAmount = 2
}
}
}
import SwiftUI
import AVFoundation
final class CustomCameraController: UIViewController {
static let shared = CustomCameraController()
private var captureSession = AVCaptureSession()
private var backCamera: AVCaptureDevice?
private var frontCamera: AVCaptureDevice?
private var currentCamera: AVCaptureDevice?
private var photoOutput: AVCapturePhotoOutput?
private var cameraPreviewLayer: AVCaptureVideoPreviewLayer?
weak var captureDelegate: AVCapturePhotoCaptureDelegate?
override func viewDidLoad() {
super.viewDidLoad()
setup()
}
func configurePreviewLayer(with frame: CGRect) {
let cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
cameraPreviewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
cameraPreviewLayer.connection?.videoOrientation = AVCaptureVideoOrientation.portrait
cameraPreviewLayer.frame = frame
view.layer.insertSublayer(cameraPreviewLayer, at: 0)
}
func startRunningCaptureSession() {
captureSession.startRunning()
}
func stopRunningCaptureSession() {
captureSession.stopRunning()
}
func takePhoto() {
let settings = AVCapturePhotoSettings()
guard let delegate = captureDelegate else {
print("delegate nil")
return
}
photoOutput?.capturePhoto(with: settings, delegate: delegate)
}
// MARK: Private
private func setup() {
setupCaptureSession()
setupDevice()
setupInputOutput()
}
private func setupCaptureSession() {
captureSession.sessionPreset = AVCaptureSession.Preset.photo
}
private func setupDevice() {
let deviceDiscoverySession = AVCaptureDevice.DiscoverySession(
deviceTypes: [.builtInWideAngleCamera],
mediaType: .video,
position: .unspecified
)
for device in deviceDiscoverySession.devices {
switch device.position {
case AVCaptureDevice.Position.front:
frontCamera = device
case AVCaptureDevice.Position.back:
backCamera = device
default:
break
}
}
self.currentCamera = self.backCamera
}
private func setupInputOutput() {
do {
guard let currentCamera = currentCamera else { return }
let captureDeviceInput = try AVCaptureDeviceInput(device: currentCamera)
captureSession.addInput(captureDeviceInput)
photoOutput = AVCapturePhotoOutput()
photoOutput?.setPreparedPhotoSettingsArray(
[AVCapturePhotoSettings(format: [AVVideoCodecKey: AVVideoCodecType.hevc])],
completionHandler: nil
)
guard let photoOutput = photoOutput else { return }
captureSession.addOutput(photoOutput)
} catch {
print(error)
}
}
}
struct CustomCameraRepresentable: UIViewControllerRepresentable {
// #Environment(\.presentationMode) var presentationMode
init(cameraFrame: CGRect, imageCompletion: #escaping ((UIImage) -> Void)) {
self.cameraFrame = cameraFrame
self.imageCompletion = imageCompletion
}
#State var cameraFrame: CGRect
#State var imageCompletion: ((UIImage) -> Void)
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
func makeUIViewController(context: Context) -> CustomCameraController {
CustomCameraController.shared.configurePreviewLayer(with: cameraFrame)
CustomCameraController.shared.captureDelegate = context.coordinator
return CustomCameraController.shared
}
func updateUIViewController(_ cameraViewController: CustomCameraController, context: Context) {}
func takePhoto() {
CustomCameraController.shared.takePhoto()
}
func startRunningCaptureSession() {
CustomCameraController.shared.startRunningCaptureSession()
}
func stopRunningCaptureSession() {
CustomCameraController.shared.stopRunningCaptureSession()
}
}
extension CustomCameraRepresentable {
final class Coordinator: NSObject, AVCapturePhotoCaptureDelegate {
private let parent: CustomCameraRepresentable
init(_ parent: CustomCameraRepresentable) {
self.parent = parent
}
func photoOutput(_ output: AVCapturePhotoOutput,
didFinishProcessingPhoto photo: AVCapturePhoto,
error: Error?) {
if let imageData = photo.fileDataRepresentation() {
guard let newImage = UIImage(data: imageData) else { return }
parent.imageCompletion(newImage)
}
// parent.presentationMode.wrappedValue.dismiss()
}
}
}

Resources