iOS Google Map SDK not scrolling smooth during zoom in/out - ios

func setMarkeronmap(isFitAllPins : Bool)
{
var myTagValue : Int = 0
var bounds = GMSCoordinateBounds()
// loop for set marker icon image
for item in self.arrFirstThirty
{
if let jsonDict = item as? NSDictionary
{
var floatLat = Double()
var floatLong = Double()
if let temp = jsonDict.value(forKey: "Latitude") as? String {
floatLat = Double ((jsonDict.value(forKey: "Latitude") as? String)!)!
floatLong = Double((jsonDict.value(forKey: "Longtitude") as? String)!)!
}
else {
floatLat = Double ((jsonDict.value(forKey: "Latitude") as? NSNumber)!)
floatLong = Double((jsonDict.value(forKey: "Longtitude") as? NSNumber)!)
}
self.Mapview.isMyLocationEnabled = true
let mapMarker = GMSMarker()
mapMarker.position = CLLocationCoordinate2DMake(floatLat
, floatLong)
bounds = bounds.includingCoordinate(mapMarker.position)
self.myImageView.image = UIImage (named: "\((jsonDict.value(forKey: "CompanyBrand") as! String).lowercased())_map_pin")
var sourceView = UIView()
sourceView = self.viewMarker
let copiedView: UIView = sourceView.copyView()
copiedView.tag = myTagValue
let image = self.image(with: copiedView)
mapMarker.icon = image
mapMarker.tracksViewChanges = true
mapMarker.map = self.Mapview
mapMarker.userData = myTagValue
mapMarker.zIndex = Int32(myTagValue)
myTagValue += 1
}
}
If I keep only above code then map scrolling is too smooth as required. But when I add below code then map scrolling is not smooth.
// loop for set marker iconView
myTagValue = 10000
for item in self.arrFirstThirty
{
if let jsonDict = item as? NSDictionary
{
var floatLat = Double()
var floatLong = Double()
if let temp = jsonDict.value(forKey: "Latitude") as? String {
floatLat = Double ((jsonDict.value(forKey: "Latitude") as? String)!)!
floatLong = Double((jsonDict.value(forKey: "Longtitude") as? String)!)!
}
else {
floatLat = Double ((jsonDict.value(forKey: "Latitude") as? NSNumber)!)
floatLong = Double((jsonDict.value(forKey: "Longtitude") as? NSNumber)!)
}
let mapMarker = GMSMarker()
mapMarker.position = CLLocationCoordinate2DMake(floatLat
, floatLong)
bounds = bounds.includingCoordinate(mapMarker.position)
Mapview.needsUpdateConstraints()
var strCurrType = String("Euro")
if Defaults[PDUserDefault.CurrencyNameEnglish] != nil && Defaults[PDUserDefault.CurrencyRate] != nil
{
strCurrType = Defaults[PDUserDefault.CurrencyNameSymbol]!
let abc = ("\((jsonDict.value(forKey: "FuelPrice") as? String)!)")
let strPrice = Float(abc.replacingOccurrences(of: ",", with: ".", options: .literal, range: nil))
var updatedPrice = Float(Defaults[PDUserDefault.CurrencyRate]!)! * strPrice!
updatedPrice = (updatedPrice * 1000).rounded() / 1000
var finalPrice = String.init(format: "%.3f", updatedPrice)
if Defaults[PDUserDefault.LanguageType] == "1"
{
finalPrice = finalPrice.replacingOccurrences(of: ",", with: ".")
}
else
{
finalPrice = finalPrice.replacingOccurrences(of: ".", with: ",")
}
self.lblmarkername.text = ("\(finalPrice) \(strCurrType!)")
let strokeTextAttributes = [
NSStrokeColorAttributeName : UIColor.init(red: 1.0/255.0, green: 123.0/255.0, blue: 146.0/255.0, alpha: 1.0),NSForegroundColorAttributeName : UIColor.white,NSStrokeWidthAttributeName : -3.0,] as [String : Any]
self.lblmarkername.attributedText = NSAttributedString(string: ("\(finalPrice) \(strCurrType!)"), attributes: strokeTextAttributes)
}
else
{
strCurrType = Defaults[PDUserDefault.CurrencyNameSymbol]!
let abc = ("\((jsonDict.value(forKey: "FuelPrice") as? String)!)")
var strmyPrice = Float(abc.replacingOccurrences(of: ",", with: ".", options: .literal, range: nil))
strmyPrice = (strmyPrice! * 1000).rounded() / 1000
var finalPrice = String.init(format: "%.3f", strmyPrice!)
if Defaults[PDUserDefault.LanguageType] == "1"
{
finalPrice = finalPrice.replacingOccurrences(of: ",", with: ".")
}
else
{
finalPrice = finalPrice.replacingOccurrences(of: ".", with: ",")
}
self.lblmarkername.text = ("\(finalPrice) \(strCurrType!)")
let strokeTextAttributes = [
NSStrokeColorAttributeName : UIColor.init(red: 1.0/255.0, green: 123.0/255.0, blue: 146.0/255.0, alpha: 1.0),NSForegroundColorAttributeName : UIColor.white,NSStrokeWidthAttributeName : -3.0,] as [String : Any]
self.lblmarkername.attributedText = NSAttributedString(string: ("\(finalPrice) \(strCurrType!)"), attributes: strokeTextAttributes)
}
var sourceView = UIView()
sourceView = self.viewMarker2
let copiedView: UIView = sourceView.copyView()
copiedView.tag = myTagValue
mapMarker.iconView = copiedView
mapMarker.tracksViewChanges = true
mapMarker.map = self.Mapview
mapMarker.userData = myTagValue
mapMarker.zIndex = Int32(myTagValue)
myTagValue += 1
markerArray.add(mapMarker)
}
}
if isFitAllPins
{
let update = GMSCameraUpdate.fit(bounds, withPadding: 80)
self.Mapview.animate(with: update)
}
}
Using first loop, I had added marker with image only. And then in second loop I have added prices over the market image because I have to keep prices always on top without hiding behind the marker image. But if I add prices this way, Map scrolling is not smooth. So please anyone can help me on this?

Related

Google map showing skin color screen

Viewdidload method
override func viewDidLoad() {
super.viewDidLoad()
selectedAdressType = "pickup"
mapV.delegate = self
styleOfMap()
mapV.settings.myLocationButton = true
mapV.isMyLocationEnabled = true
// self.pickupAddressTxtF.setLeftPaddingPoints(7)
//self.destinationAddressTxtF.setLeftPaddingPoints(7)
googleLocationTableV.rowHeight = UITableViewAutomaticDimension
googleLocationTableV.estimatedRowHeight = 50
googleLocationTableV.tableFooterView = UIView(frame: .zero)
savedLocationTableV.keyboardDismissMode = .interactive
googleLocationTableV.keyboardDismissMode = .interactive
// Add footer for set pinn location
googleLocationTableV.tableFooterView = AddFooterView()
savedLocationTableV.tableFooterView = AddFooterView()
redirectMap(to: Double("\(latitude)") ?? 0.0, lng: Double("\(longitude)") ?? 0.0)
let userDefaults = UserDefaults.standard
if userDefaults.object(forKey: "LocationInitialData") != nil {
let retriveArrayData = UserDefaults.standard.object(forKey: "LocationInitialData") as? NSData
let dataDic = (NSKeyedUnarchiver.unarchiveObject(with: retriveArrayData! as Data) as? NSDictionary)
print(" saved response = \(dataDic!)")
self.recentSerchList = dataDic?.value(forKey: "recent_search")as! NSArray
self.savedLocationList = dataDic?.value(forKey: "save_address")as! NSArray
self.savedLocationTableV.reloadData()
}
savedLocationTableV.keyboardDismissMode = .onDrag
googleLocationTableV.keyboardDismissMode = .onDrag
// addressSerachApiCall()
if Language.isRTL {
pickupAddressTxtF.textAlignment = .right
}
}
Redirect Method
func redirectMap(to lat: Double, lng: Double) {
// [_mapView clear];
let position: CLLocationCoordinate2D = CLLocationCoordinate2DMake(lat, lng)
let camera = GMSCameraPosition(target: position, zoom: 16, bearing: 0, viewingAngle: 0)
mapV.animate(to: camera)
}
Styling of map show "Unable to find style.json"
func styleOfMap() {
do {
if let styleURL = Bundle.main.url(forResource: "style", withExtension: "json") {
mapV.mapStyle = try GMSMapStyle(contentsOfFileURL: styleURL)
} else {
NSLog("Unable to find style.json")
}
} catch {
NSLog("One or more of the map styles failed to load. \(error)")
}
}

UIlabel() tap to expand text

So I am building a map that provides factual data on a variety of points located in Washington DC. The data is getting pulled from a geojson and listed as 20 or so points. When you click on the point, there is a popup that occurs that shows a truncated version of the text, only 10 lines. However some of the text is really long, 200 lines+. I want to develop a way to expand the text, or have a scrollable bar. Basically anything to have the option to open up the text. Adding an image to show what it looks like.
You can see an image here: https://i.stack.imgur.com/yeeBa.jpg
Here is the code I am using.
The data is getting called in the ViewController.swift using this
func loadInitialData() {
// 1
guard let fileName = Bundle.main.path(forResource: "PublicArt4", ofType: "json")
else { return }
let optionalData = try? Data(contentsOf: URL(fileURLWithPath: fileName))
guard
let data = optionalData,
// 2
let json = try? JSONSerialization.jsonObject(with: data),
// 3
let dictionary = json as? [String: Any],
// 4
let works = dictionary["data"] as? [[Any]]
else { return }
// 5
let validWorks = works.compactMap { Artwork(json: $0) }
artworks.append(contentsOf: validWorks)
}
}
The artworkViews.swift displays the label formatting
let detailLabel = UILabel()
detailLabel.text = artwork.locationlink
detailLabel.text = artwork.subtitle
detailCalloutAccessoryView = detailLabel
detailLabel.font = UIFont(name: "Heiti TC", size: 12)
detailLabel.numberOfLines = 10
//detailLabel.adjustsFontSizeToFitWidth = true
// detailLabel.minimumScaleFactor = 0.5
// detailLabel.baselineAdjustment = .alignCenters
// detailLabel.textAlignment = .left
The geojson is formatted like this
{"data" : [ [
1,
"Washington Monument",
"Click here to learn more",
"Madison Dr NW & 15th St NW",
"Washington",
"DC",
20001,
"38.89013",
"-77.033031",
"www.google.com Welcome to the Washington Monument,",
"Mural",
"Some text, not sure what it is",
" "
],...
The Artwork.swift pulls the data in such a way
class Artwork: NSObject, MKAnnotation {
let title: String?
let locationName: String
let locationURL: String
let discipline: String
let coordinate: CLLocationCoordinate2D
init(title: String, locationName: String, locationURL: String, discipline: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.locationName = locationName
self.locationURL = locationURL
self.discipline = discipline
self.coordinate = coordinate
super.init()
}
var subtitle: String? {
return locationName
}
var locationlink: String? {
return locationURL
}
init?(json: [Any]) {
// 1
if let title = json[2] as? String {
self.title = title
} else {
self.title = "No Title"
}
// json[11] is the long description
//self.locationName = json[11] as! String
// json[12] is the short location string
self.locationName = json[9] as! String
self.discipline = json[10] as! String
self.locationURL = json[3] as! String
// 2
if let latitude = Double(json[7] as! String),
let longitude = Double(json[8] as! String) {
self.coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
} else {
self.coordinate = CLLocationCoordinate2D()
}
}
// pinTintColor for disciplines: Sculpture, Plaque, Mural, Monument, other
var markerTintColor: UIColor {
switch discipline {
case "Monument":
return .red
case "Mural":
return .cyan
case "Plaque":
return .blue
case "Sculpture":
return .purple
default:
return .green
}
}
var imageName: String? {
if discipline == "Mural" { return "Flag" }
return "Flag"
}
// Annotation right callout accessory opens this mapItem in Maps app
func mapItem() -> MKMapItem {
let addressDict = [CNPostalAddressStreetKey: subtitle!]
let placemark = MKPlacemark(coordinate: coordinate, addressDictionary: addressDict)
let mapItem = MKMapItem(placemark: placemark)
mapItem.name = title
return mapItem
}
}
The one thing I tried was to switch it to a UITextView, but i cant get that to load properly. Mainly because I am not sure how to integrate it into the ViewController.
let detailLabel = UITextView()
detailLabel.text = artwork.locationlink
detailLabel.text = artwork.subtitle
detailLabel.textColor = UIColor.red
detailLabel.selectedTextRange = detailLabel.textRange(from: detailLabel.beginningOfDocument, to: detailLabel.beginningOfDocument)
```

Terminating app due to uncaught exception 'NSRangeException'. libc++abi.dylib: terminating with uncaught exception of type NSException

I get this crash error:
MXNet2CoreML[11383:1168377]
*** Terminating app due to uncaught exception 'NSRangeException', reason:
*** -[__NSArray0 objectAtIndex:]: index 0 beyond bounds for empty NSArray'
*** First throw call stack: (0x18333f164 0x182588528 0x18329a020 0x1007894bc 0x1011ef94c 0x1011b0420 0x1021412cc 0x10214128c
0x102145ea0 0x1832e7344 0x1832e4f20 0x183204c58 0x1850b0f84
0x18c95d5c4 0x1007d8894 0x182d2456c)
libc++abi.dylib: terminating with uncaught exception of type
NSException
Here is the Complete Code
//Owais: - Map setup
func resetRegion(){
let region = MKCoordinateRegionMakeWithDistance(annotation.coordinate, 5000, 5000)
mapKit.setRegion(region, animated: true)
}
var myLatitude = ""
var myLongitude = ""
// Array of annotations
let annotation = MKPointAnnotation()
var places = PredictionLocationList().place
var locationsArray = [String]()
var ie: Int = 0
#IBOutlet var mapKit: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let defaults = UserDefaults.standard
let data = defaults.data(forKey: "selectedimage")
let uiimage2 = UIImage(data: data!)
defaults.synchronize()
let image = uiimage2
// imageView.image = image
predictUsingVision(image: image!)
let swipeDown = UISwipeGestureRecognizer(target: self, action: #selector(respondToSwipeGesture))
swipeDown.direction = UISwipeGestureRecognizerDirection.down
self.view.addGestureRecognizer(swipeDown)
// Do any additional setup after loading the view.
}
#objc func respondToSwipeGesture(gesture: UIGestureRecognizer) {
if let swipeGesture = gesture as? UISwipeGestureRecognizer {
switch swipeGesture.direction {
case UISwipeGestureRecognizerDirection.right:
print("Swiped right")
case UISwipeGestureRecognizerDirection.down:
print("Swiped down")
self.dismiss(animated: true, completion: nil)
case UISwipeGestureRecognizerDirection.left:
print("Swiped left")
case UISwipeGestureRecognizerDirection.up:
print("Swiped up")
default:
break
}
}
}
func predictUsingVision(image: UIImage) {
guard let visionModel = try? VNCoreMLModel(for: model.model) else {
fatalError("Something went wrong")
}
let request = VNCoreMLRequest(model: visionModel) { request, error in
if let observations = request.results as? [VNClassificationObservation] {
let top3 = observations.prefix(through: 2)
.map { ($0.identifier, Double($0.confidence)) }
self.show(results: top3)
}
}
request.imageCropAndScaleOption = .centerCrop
let handler = VNImageRequestHandler(cgImage: image.cgImage!)
try? handler.perform([request])
}
typealias Prediction = (String, Double)
func show(results: [Prediction]) {
var s: [String] = []
for (i, pred) in results.enumerated() {
let latLongArr = pred.0.components(separatedBy: "\t")
print("lat long \(latLongArr)")
myLatitude = latLongArr[1]
myLongitude = latLongArr[2]
ie = i
s.append(String(format: "%d: %# %# (%.2f%%)", i + 1, myLatitude, myLongitude, pred.1 * 100))
LocationByCoordinates(latitude: myLatitude, longitude: myLongitude)
// let ew1 = (String(format: "%.2f%", pred.1 * 100))
// print(ew1)
// let double1 = Double(ew1)
// self.doubles.append(double1!)
// print("eueue \(self.doubles)")
// let maxDouble = max(max(double1, double2), double3)
print("first latidue \(myLatitude),,,, \(myLongitude)")
places[i].title = String(i+1)
places[i].coordinate = CLLocationCoordinate2D(latitude: CLLocationDegrees(myLatitude)!, longitude: CLLocationDegrees(myLongitude)!)
}
// predictionLabel.text = s.joined(separator: "\n")
// Map reset
resetRegion()
mapKit.centerCoordinate = places[0].coordinate
// Show annotations for the predictions on the map
mapKit.addAnnotations(places)
// Zoom map to fit all annotations
zoomMapFitAnnotations()
}
func zoomMapFitAnnotations() {
var zoomRect = MKMapRectNull
for annotation in mapKit.annotations {
let annotationPoint = MKMapPointForCoordinate(annotation.coordinate)
let pointRect = MKMapRectMake(annotationPoint.x, annotationPoint.y, 0, 0)
if (MKMapRectIsNull(zoomRect)) {
zoomRect = pointRect
} else {
zoomRect = MKMapRectUnion(zoomRect, pointRect)
}
}
self.mapKit.setVisibleMapRect(zoomRect, edgePadding: UIEdgeInsetsMake(50, 50, 50, 50), animated: true)
}
func LocationByCoordinates (latitude: String,longitude:String) {
let mapsKey = UserDefaults.standard.string(forKey: "maps_key") ?? ""
Alamofire.request("https://maps.googleapis.com/maps/api/geocode/json?latlng=\(latitude),\(longitude)&key=\(mapsKey)").responseJSON { response in
if let json = response.result.value {
let request = json as? NSDictionary
if let id = request!["results"] {
// print(id)
let ide = id as? NSArray
let formatted_address = ide![0]
let fors = formatted_address as! NSDictionary
//print(fors.value(forKey: "formatted_address"))
let forss = fors.value(forKey: "formatted_address")
self.locationsArray.append(forss as? String ?? "")
if self.ie == 0 {
self.places[0].identifier = (forss as? String)!
} else if self.ie == 1 {
self.places[1].identifier = (forss as? String)!
} else if self.ie == 2 {
self.places[2].identifier = (forss as? String)!
}
}
}
}
}
}
extension Collection where Indices.Iterator.Element == Index {
subscript (safe index: Index) -> Iterator.Element? {
return indices.contains(index) ? self[index] : nil
}
}
The problem is in this line let formatted_address = ide![0]
If an array is empty, you can't get it's first element with [0] because there is no element at all!
All you have to do is check if the array is not empty before unwrapping it:
if ide!.count > 0 {
//Your code
}
To avoid force-unwrapping ide, you may want to use optional binding like so:
if let id = request!["results"], let ide = id as? NSArray {
let formatted_address = ide[0]
let fors = formatted_address as! NSDictionary
//rest of your code
}

iOS Charts, line chart doesn't refresh datas

I have this problem:
everytime I switch between the segments of the segmented control the datas are messed up, but only if I switch in runtime and only in the 0 segment, the 1 segment works flawlessly no matter how many time I switch. Here's a gif that explains what I mean:
How to deal with this problem? Here's some code
the ibaction func of the segmented control
#IBAction func timeSelectionSegControl(_ sender: UISegmentedControl) {
switch sender.selectedSegmentIndex {
case 0:
last7Filler()
case 1:
todayFiller()
case 2:
last30Filler()
default:
break
} }
todayFiller() (the one which works well):
func todayFiller() {
emptyArrays()
low = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Bassa intensità")
mid = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Media intensità")
high = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Alta intensità")
for i in low {
if i.date == currentDate() { //put in arrays xxxA the elements which have the property .date equals to today
lowA.append(i)
}
}
for i in mid {
if i.date == currentDate() {
midA.append(i)
}
}
for i in high {
if i.date == currentDate() {
highA.append(i)
}
}
//low severity fill funcs FOR TIME
if !lowA.isEmpty || !midA.isEmpty || !highA.isEmpty {
fillBarChart()
var lowSeverityDataEntry = [ChartDataEntry]()
let crossReferenceLow = lowA.reduce(into: [String: [Cig]]()) {
$0[$1.time!.returnFirstNcharacters(n: 3), default: []].append($1)}
let sortedKeysLow = Array(crossReferenceLow.keys).sorted(by: <)
for key in sortedKeysLow {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no elements found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let indexString = matchingTerms[0]
let first3 = indexString.returnFirstNcharacters(n: 3)
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceLow[first3]?.count)!))
lowSeverityDataEntry.append(value)
doubleArray.append(Double(index!))
doubleArrayMax.append(Double((crossReferenceLow[first3]?.count)!))
}
}
let lowSeverityLine = LineChartDataSet(values: lowSeverityDataEntry, label: "Bassa")
lowSeverityLine.colors = [NSUIColor.green]
lowSeverityLine.mode = .cubicBezier
lowSeverityLine.lineCapType = self.lineCap
lowSeverityLine.lineWidth = self.lineWidth
lowSeverityLine.circleRadius = self.circleRadius
lowSeverityLine.circleColors = [NSUIColor.green]
lowSeverityLine.circleHoleColor = NSUIColor.white
lowSeverityLine.circleHoleRadius = self.circleHoleRadius
lowSeverityLine.drawValuesEnabled = false
//mid severity fill funcs FOR TIME
var midSeverityDataEntry = [ChartDataEntry]()
let crossReferenceMid = midA.reduce(into: [String: [Cig]]()) {
$0[$1.time!.returnFirstNcharacters(n: 3), default: []].append($1)}
let sortedKeysMid = Array(crossReferenceMid.keys).sorted(by: <)
for key in sortedKeysMid {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no elements found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let indexString = matchingTerms[0]
let first3 = indexString.returnFirstNcharacters(n: 3)
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceMid[first3]?.count)!))
midSeverityDataEntry.append(value)
doubleArray.append(Double(index!))
doubleArrayMax.append(Double((crossReferenceMid[first3]?.count)!))
}
}
let midSeverityLine = LineChartDataSet(values: midSeverityDataEntry, label: "Media")
midSeverityLine.colors = [NSUIColor.yellow]
midSeverityLine.mode = .cubicBezier
midSeverityLine.lineCapType = self.lineCap
midSeverityLine.lineWidth = self.lineWidth
midSeverityLine.circleRadius = self.circleRadius
midSeverityLine.circleColors = [NSUIColor.yellow]
midSeverityLine.circleHoleColor = NSUIColor.white
midSeverityLine.circleHoleRadius = self.circleHoleRadius
midSeverityLine.drawValuesEnabled = false
//high severity fill funcs FOR TIME
var highSeverityDataEntry = [ChartDataEntry]()
let crossReferenceHigh = highA.reduce(into: [String: [Cig]]()) {
$0[$1.time!.returnFirstNcharacters(n: 3), default: []].append($1)}
let sortedKeysHigh = Array(crossReferenceHigh.keys).sorted(by: <)
for key in sortedKeysHigh {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no elements found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let indexString = matchingTerms[0]
let first3 = indexString.returnFirstNcharacters(n: 3)
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceHigh[first3]?.count)!))
highSeverityDataEntry.append(value)
doubleArray.append(Double(index!))
doubleArrayMax.append(Double((crossReferenceHigh[first3]?.count)!))
}
}
let highSeverityLine = LineChartDataSet(values: highSeverityDataEntry, label: "Alta")
highSeverityLine.colors = [NSUIColor.red]
highSeverityLine.mode = .cubicBezier
highSeverityLine.lineCapType = self.lineCap
highSeverityLine.lineWidth = self.lineWidth
highSeverityLine.circleRadius = self.circleRadius
highSeverityLine.circleColors = [NSUIColor.red]
highSeverityLine.circleHoleColor = NSUIColor.white
highSeverityLine.circleHoleRadius = self.circleHoleRadius
highSeverityLine.drawValuesEnabled = false
let data = LineChartData()
data.addDataSet(lowSeverityLine)
data.addDataSet(midSeverityLine)
data.addDataSet(highSeverityLine)
lineChartView.data = data
guard let min = doubleArray.min() else {return}
guard let max = doubleArrayMax.max() else {return}
lineChartView.xAxis.axisMinimum = min
lineChartView.leftAxis.axisMaximum = max
let myXaxis = lineChartView.xAxis
myXaxis.granularity = 1.0
myXaxis.axisMaximum = Double(values.count)
myXaxis.labelPosition = .bottom
myXaxis.gridLineWidth = 0.2
myXaxis.axisLineWidth = 1.0
myXaxis.axisLineColor = UIColor.black
myXaxis.valueFormatter = IndexAxisValueFormatter(values: values)
let yLeftAxis = lineChartView.leftAxis
yLeftAxis.granularity = 1.0
yLeftAxis.axisMinimum = 0.0
yLeftAxis.gridLineWidth = 0.2
yLeftAxis.axisLineWidth = 1.0
yLeftAxis.axisLineColor = UIColor.black
lineChartView.rightAxis.enabled = false
lineChartView.chartDescription?.text = ""
lineChartView.animate(xAxisDuration: 1.0, easingOption: .linear)
} else {
lineChartView.noDataText = "Nessuna sigaretta fumata oggi"
barChartView.noDataText = "Nessuna sigaretta fumata oggi"
} }
last7Filler() func (the one which give me problems):
func last7Filler() {
emptyArrays()
low = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Bassa intensità")
mid = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Media intensità")
high = CoreDataController.shared.loadCigFromSeverityNumber(severity: "Alta intensità")
for i in low {
let first3Date = i.date!.returnFirstNcharacters(n: 3)
for a in past7Days() {
let first3Day = "\(a)-"
if first3Date == first3Day {
lowA.append(i)
}
}
}
for i in mid {
let first3Date = i.date!.returnFirstNcharacters(n: 3)
for a in past7Days() {
let first3Day = "\(a)-"
if first3Date == first3Day {
midA.append(i)
}
}
}
for i in high {
let first3Date = i.date!.returnFirstNcharacters(n: 3)
for a in past7Days() {
let first3Day = "\(a)-"
if first3Date == first3Day {
highA.append(i)
}
}
}
if !lowA.isEmpty || !midA.isEmpty || !highA.isEmpty {
fillBarChart()
var lowSeverityDataEntry = [ChartDataEntry]()
let crossReferenceLow = lowA.reduce(into: [String:[Cig]]() ) {
$0[$1.date!.returnFirstNcharacters(n: 5), default: []].append($1)}
let sortedKeysLow = Array(crossReferenceLow.keys).sorted(by: <)
for key in sortedKeysLow {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no elements found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceLow[key]?.count)!))
lowSeverityDataEntry.append(value)
}
}
let lowSeverityLine = LineChartDataSet(values: lowSeverityDataEntry, label: "Bassa")
lowSeverityLine.colors = [NSUIColor.green]
lowSeverityLine.mode = .cubicBezier
lowSeverityLine.lineCapType = self.lineCap
lowSeverityLine.lineWidth = self.lineWidth
lowSeverityLine.circleRadius = self.circleRadius
lowSeverityLine.circleColors = [NSUIColor.green]
lowSeverityLine.circleHoleColor = NSUIColor.white
lowSeverityLine.circleHoleRadius = self.circleHoleRadius
lowSeverityLine.drawValuesEnabled = false
var midSeverityDataEntry = [ChartDataEntry]()
let crossReferenceMid = midA.reduce(into: [String: [Cig]]() ) {
$0[$1.date!.returnFirstNcharacters(n: 5), default: []].append($1)}
let sortedKeysMid = Array(crossReferenceMid.keys).sorted(by: <)
for key in sortedKeysMid {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no elements found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceMid[key]?.count)!))
midSeverityDataEntry.append(value)
}
}
let midSeverityLine = LineChartDataSet(values: midSeverityDataEntry, label: "Media")
midSeverityLine.colors = [NSUIColor.yellow]
midSeverityLine.mode = .cubicBezier
midSeverityLine.lineCapType = self.lineCap
midSeverityLine.lineWidth = self.lineWidth
midSeverityLine.circleRadius = self.circleRadius
midSeverityLine.circleColors = [NSUIColor.yellow]
midSeverityLine.circleHoleColor = NSUIColor.white
midSeverityLine.circleHoleRadius = self.circleHoleRadius
midSeverityLine.drawValuesEnabled = false
var highSeverityDataEntry = [ChartDataEntry]()
let crossReferenceHigh = highA.reduce(into: [String: [Cig]]()) {
$0[$1.date!.returnFirstNcharacters(n: 5), default: []].append($1)}
let sortedKeysHigh = Array(crossReferenceHigh.keys).sorted(by: <)
for key in sortedKeysHigh {
let matchingTerms = values.filter({
$0.range(of: key, options: .caseInsensitive) != nil
})
if matchingTerms.isEmpty {
print("no element found in \(key)")
} else {
let index = values.index(of: matchingTerms[0])
let value = ChartDataEntry(x: Double(index!), y: Double((crossReferenceHigh[key]?.count)!))
highSeverityDataEntry.append(value)
}
}
let highSeverityLine = LineChartDataSet(values: highSeverityDataEntry, label: "Alta")
highSeverityLine.colors = [NSUIColor.red]
highSeverityLine.mode = .cubicBezier
highSeverityLine.lineCapType = self.lineCap
highSeverityLine.lineWidth = self.lineWidth
highSeverityLine.circleRadius = self.circleRadius
highSeverityLine.circleColors = [NSUIColor.red]
highSeverityLine.circleHoleColor = NSUIColor.white
highSeverityLine.circleHoleRadius = self.circleHoleRadius
highSeverityLine.drawValuesEnabled = false
let data = LineChartData()
data.addDataSet(lowSeverityLine)
data.addDataSet(midSeverityLine)
data.addDataSet(highSeverityLine)
lineChartView.data = data
lineChartView.notifyDataSetChanged()
lineChartView.data!.notifyDataChanged()
//graphic configuration
let myXaxis = lineChartView.xAxis
myXaxis.granularity = 1.0
myXaxis.axisMinimum = 0.0
myXaxis.axisMaximum = Double(values.count)
myXaxis.labelPosition = .bottom
myXaxis.gridLineWidth = 0.2
myXaxis.axisLineWidth = 1.0
myXaxis.axisLineColor = UIColor.black
myXaxis.valueFormatter = IndexAxisValueFormatter(values: values)
let yLeftAxis = lineChartView.leftAxis
yLeftAxis.granularity = 1.0
yLeftAxis.axisMinimum = 0.0
yLeftAxis.gridLineWidth = 0.2
yLeftAxis.axisLineWidth = 1.0
yLeftAxis.axisLineColor = UIColor.black
lineChartView.rightAxis.enabled = false
lineChartView.chartDescription?.text = ""
lineChartView.animate(xAxisDuration: 1.0, easingOption: .linear)
} else {
lineChartView.noDataText = "Nessuna sigaretta fumata nei scorsi 7 giorni"
barChartView.noDataText = "Nessuna sigaretta fumata nei scorsi 7 giorni"
}
p.s. the func emptyArrays() just gives removeAll() to the arrays low, mid, high, lowA, midA and highA

iOS Charts Bubble Chart Colors

How can I set specific colors for an individual bubble in an iOS Charts bubble chart dependent on its y-value? Just like it is shown in one of the screenshot on the iOS Charts GitHub site here.
So all top level bubbles should be the same color, all middle ones another, etc...
I already tried to change the color like that:
chartDataSet.colors = [UIColor.red, UIColor.orange, UIColor.green]
But that only changes the color for every x value.
Full Code
That is my full code for setting the bubble chart:
func getDetailedAnalytics() {
let currentTeacherDetailedAnalyticsUrl = TEACHER_DETAILED_ANALYTICS_URL + currentTeacherCourseId
Alamofire.request(currentTeacherDetailedAnalyticsUrl).responseJSON { response in
let result = response.result
if let dict = result.value as? Dictionary<String, AnyObject> {
let firstDateValue = dict["0"] as? Dictionary<String, AnyObject>
let secondDateValue = dict["1"] as? Dictionary<String, AnyObject>
let thirdDateValue = dict["2"] as? Dictionary<String, AnyObject>
let fourthDateValue = dict["3"] as? Dictionary<String, AnyObject>
let fifthDateValue = dict["4"] as? Dictionary<String, AnyObject>
let firstDateLow = firstDateValue?["numberValuesUnder50"] as! Int
let firstDateMiddle = firstDateValue?["numberValuesInMiddle"] as! Int
let firstDateHigh = firstDateValue?["numberValuesOver75"] as! Int
let secondDateLow = secondDateValue?["numberValuesUnder50"] as! Int
let secondDateMiddle = secondDateValue?["numberValuesInMiddle"] as! Int
let secondDateHigh = secondDateValue?["numberValuesOver75"] as! Int
let thirdDateLow = thirdDateValue?["numberValuesUnder50"] as! Int
let thirdDateMiddle = thirdDateValue?["numberValuesInMiddle"] as! Int
let thirdDateHigh = thirdDateValue?["numberValuesOver75"] as! Int
let fourthDateLow = fourthDateValue?["numberValuesUnder50"] as! Int
let fourthDateMiddle = fourthDateValue?["numberValuesInMiddle"] as! Int
let fourthDateHigh = fourthDateValue?["numberValuesOver75"] as! Int
let fifthDateLow = fifthDateValue?["numberValuesUnder50"] as! Int
let fifthDateMiddle = fifthDateValue?["numberValuesInMiddle"] as! Int
let fifthDateHigh = fifthDateValue?["numberValuesOver75"] as! Int
self.analyticsData = [25, 62, 88, 25, 62, 88, 25, 62, 88, 25, 62, 88, 25, 62, 88]
self.analyticsAmountsData = [firstDateLow, firstDateMiddle, firstDateHigh, secondDateLow, secondDateMiddle, secondDateHigh, thirdDateLow, thirdDateMiddle, thirdDateHigh, fourthDateLow, fourthDateMiddle, fourthDateHigh, fifthDateLow, fifthDateMiddle, fifthDateHigh]
self.setBubbleChart(dataPoints: self.dateBubble, values: self.analyticsData, amounts: self.analyticsAmountsData)
}
}
}
func setBubbleChart(dataPoints: [Int], values: [Int], amounts: [Int]) {
var dataEntries: [BubbleChartDataEntry] = []
for i in 0..<dataPoints.count {
let dataEntry = BubbleChartDataEntry(x: Double(dataPoints[i]), y: Double(values[i]), size: (CGFloat(amounts[i])))
dataEntries.append(dataEntry)
}
let format = NumberFormatter()
format.generatesDecimalNumbers = false
format.zeroSymbol = ""
let formatter = DefaultValueFormatter(formatter: format)
let chartDataSet = BubbleChartDataSet(values: dataEntries, label: "")
let chartData = BubbleChartData(dataSet: chartDataSet)
bubbleView.doubleTapToZoomEnabled = false
bubbleView.scaleXEnabled = false
bubbleView.scaleYEnabled = false
bubbleView.highlightPerTapEnabled = false
bubbleView.highlightPerDragEnabled = false
let firstLegend = LegendEntry.init(label: "Below 50", form: .default, formSize: CGFloat.nan, formLineWidth: CGFloat.nan, formLineDashPhase: CGFloat.nan, formLineDashLengths: nil, formColor: UIColor.black)
let secondLegend = LegendEntry.init(label: "Between 50 and 75", form: .default, formSize: CGFloat.nan, formLineWidth: CGFloat.nan, formLineDashPhase: CGFloat.nan, formLineDashLengths: nil, formColor: UIColor.black)
let thirdLegend = LegendEntry.init(label: "Over 75", form: .default, formSize: CGFloat.nan, formLineWidth: CGFloat.nan, formLineDashPhase: CGFloat.nan, formLineDashLengths: nil, formColor: UIColor.black)
bubbleView.chartDescription = nil
bubbleView.legend.entries = [firstLegend, secondLegend, thirdLegend]
bubbleView.data = chartData
bubbleView.animate(xAxisDuration: 2.0, yAxisDuration: 2.0)
let xAxis: XAxis = bubbleView.xAxis
xAxis.drawAxisLineEnabled = false
xAxis.drawGridLinesEnabled = false
xAxis.drawLabelsEnabled = false
xAxis.axisMinimum = 0
xAxis.axisMaximum = 10
let leftAxis: YAxis = bubbleView.leftAxis
leftAxis.drawAxisLineEnabled = false
leftAxis.drawGridLinesEnabled = false
leftAxis.setLabelCount(2, force: true)
leftAxis.axisMinimum = 0
leftAxis.axisMaximum = 100
let rightAxis: YAxis = bubbleView.rightAxis
rightAxis.drawAxisLineEnabled = false
rightAxis.drawGridLinesEnabled = false
rightAxis.drawLabelsEnabled = false
}
All bubbles with the value 25 should be the same color (e.g. red), all bubbles with the value 62 should be the same color (e.g. orange), and all bubbles with the value 88 should be the same color (e.g. green).
Take a look at my answer over here: Different colors for bars in BarChart depend on value
But essentially you need to write a custom function that returns a UIColor depending on the value.
func setColor(value: Double) -> UIColor{
if(value < 30){
return UIColor.red
}
else if(value <= 70 && value >= 30){
return UIColor.orange
}
else if(value > 70){
return UIColor.green
}
else { //In case anything goes wrong
return UIColor.black
}
}
And then set the colors like this
chartDataSet.colors.append(setColor(dataValue))
Hope this helps!

Resources