Post not yet marked as solved
I added multiple MKPolyline to a MKMapView. It looks fine on the beginning, but when I start zooming, the border of the MKPolyline gets lost on some poly lines.
zoomed out:
zoomed in:
here is the code I used:
import MapKit
import UIKit
class ViewController: UIViewController {
@IBOutlet weak var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
mapView.mapType = .mutedStandard
mapView.pointOfInterestFilter = .excludingAll
let tiles = [
MapTile(x: 8586, y: 5514),
MapTile(x: 8587, y: 5514),
MapTile(x: 8588, y: 5514),
MapTile(x: 8587, y: 5515),
]
let polygons = tiles.map { tile in
MKPolygon(coordinates: tile.locations, count: tile.locations.count)
}
mapView.addOverlays(polygons, level: .aboveLabels)
}
}
extension ViewController: MKMapViewDelegate {
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
if let mapTile = overlay as? MKPolygon {
let renderer = MKPolygonRenderer(polygon: mapTile)
renderer.fillColor = .systemBlue.withAlphaComponent(0.15)
renderer.strokeColor = .systemBlue
renderer.lineWidth = 0.7
return renderer
}
return MKOverlayRenderer(overlay: overlay)
}
}
struct MapTile: Identifiable, Hashable {
let x: Int
let y: Int
var id: String { "\(x)-\(y)" }
static let zoomFactor = Double(1 << 14)
static func locationOrigin(tileX: Int, tileY: Int) -> CLLocationCoordinate2D {
let lon = (Double(tileX) / MapTile.zoomFactor) * 360.0 - 180.0
let lat = atan( sinh (.pi - (Double(tileY) / MapTile.zoomFactor) * 2 * Double.pi)) * (180.0 / .pi)
return CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
var locationOrigin: CLLocationCoordinate2D {
MapTile.locationOrigin(tileX: x, tileY: y)
}
var locations: [CLLocationCoordinate2D] {
let topLeft = locationOrigin
let topRight = MapTile.locationOrigin(tileX: x + 1, tileY: y)
let bottomRight = MapTile.locationOrigin(tileX: x + 1, tileY: y + 1)
let bottomLeft = MapTile.locationOrigin(tileX: x, tileY: y + 1)
return [
topLeft,
topRight,
bottomRight,
bottomLeft
]
}
}
any ideas why this happens?
Post not yet marked as solved
I want to see all the public transport stations on my MKMapView.
With the following code I get train stations on the map:
mapView.pointOfInterestFilter = MKPointOfInterestFilter(including: [.publicTransport])
But the bus stops are missing.
If I use the Apple Maps app and chose Transit as the map, the bus stops are available there.
How can I get the bus stops in my app using MKMapView?
Post not yet marked as solved
With a Safari extension I add a link to certain websites to open the Maps app with coordinates found on the website.
In the content script I detect clicks on my added link and forward the message to the background script with browser.runtime.sendMessage({ coordinates: "some coordinates I found on the website" }).
The background script receives this message in its listener function and forwards the message to the extension handler like so
browser.runtime.onMessage.addListener((request, sender, sendResponse) => {
browser.runtime.sendNativeMessage( { message: request.coordinates }, function(response) {
});
}
The extension handler receives the message in its func beginRequest(with context: NSExtensionContext) function as expected.
In this function I convert the coordinates I receive to a valid CLLocationCoordinate2D object.
However, if I want to use the following code inside the beginRequest function to open the Maps app, it does not work on iOS. The same code works fine with a macOS Safari extension.
MKMapItem(placemark: MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 50.1234, longitude: 8.1234))).openInMaps()
Post not yet marked as solved
I wanted to use the new CLLocationButton.
It works fine as long as the iPhone language is set to English, but when I change it to German, the button is empty and not presenting the location access message anymore.
I already filed a feedback: FB9951235
Are there any workarounds?
I was playing around a bit with the new AttributedString and a few questions came up.
I saw this other forum question "JSON encoding of AttributedString with custom attributes", but I did not completely understand the answer and how I would need to use it.
I created my custom attribute where I just want to store additional text like this:
enum AdditionalTextAttribute: CodableAttributedStringKey, MarkdownDecodableAttributedStringKey {
typealias Value = AttributedString
static let name = "additionalText"
}
I then extended the AttributeScopes like this:
extension AttributeScopes {
struct MyAppAttributes: AttributeScope {
let additionalText: AdditionalTextAttribute
let swiftUI: SwiftUIAttributes
}
var myApp: MyAppAttributes.Type { MyAppAttributes.self }
}
and I also implemented the AttributeDynamicLookup like this:
extension AttributeDynamicLookup {
subscript<T: AttributedStringKey>(dynamicMember keyPath: KeyPath<AttributeScopes.MyAppAttributes, T>) -> T { self[T.self] }
}
So next I created my AttributedString and added some attributes to it:
var attStr = AttributedString("Hello, here is some text.")
let range1 = attStr.range(of: "Hello")!
let range2 = attStr.range(of: "text")!
attStr[range1].additionalText = AttributedString("Hi")
attStr[range2].foregroundColor = .blue
attStr[range2].font = .caption2
Next I tried to create some JSON from my string and took a look at it like this:
let jsonData = try JSONEncoder().encode(attStr)
print(String(data: jsonData, encoding: .utf8) ?? "no data")
//print result: ["Hello",{},", here is some ",{},"text",{"SwiftUI.ForegroundColor":{},"SwiftUI.Font":{}},".",{}]
I guess it makes sense, that both SwiftUI.ForegroundColor and SwiftUI.Font are empty, because they both do not conform to Codable protocol.
My first question would be: Why does my additionalText attribute not show up here?
I next tried to extend Color to make it codable like this:
extension Color: Codable {
enum CodingKeys: CodingKey {
case red, green, blue, alpha
case desc
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
guard let cgColor = self.cgColor,
let components = cgColor.components else {
if description.isEmpty { throw CodingErrors.encoding }
try container.encode(description, forKey: .desc)
return
}
try container.encode(components[0], forKey: .red)
try container.encode(components[1], forKey: .green)
try container.encode(components[2], forKey: .blue)
try container.encode(components[3], forKey: .alpha)
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if let description = try container.decodeIfPresent(String.self, forKey: .desc) {
if description == "blue" {
self = Color.blue
return
}
throw CodingErrors.decoding
}
let red = try container.decode(CGFloat.self, forKey: .red)
let green = try container.decode(CGFloat.self, forKey: .green)
let blue = try container.decode(CGFloat.self, forKey: .blue)
let alpha = try container.decode(CGFloat.self, forKey: .alpha)
self.init(CGColor(red: red, green: green, blue: blue, alpha: alpha))
}
}
But it looks like even though Color is now codable, the encoding function does not get called when I try to put my attributed string into the JSONEncoder.
So my next question is: Does it just not work? Or do I also miss something here?
Coming to my last question: If JSONEncoder does not work, how would I store an AttributedString to disk?
Post not yet marked as solved
If I use the Apple training app for indoor swim workouts, I can see the distance by each swimming stroke style later in the Fitness app.
However, if I build my own swim app, the distance by stroke style is not listed.
What do I have to add, to have it listed there?
The information are saved correct in the HKWorkoutEvent array in the workout.
Post not yet marked as solved
If I use the Apple training app on Apple Watch, indoor trainings like swimming in a pool will also store a location which I can see later in the Fitness app on the iPhone.
My question: How do I store these location information with my own workout app on the watch?
I tried creating a HKWorkoutRoute with the HKWorkoutRouteBuilder and added only one location. It saves fine, but the location does not appear in the Fitness app.
If I query a workout recorded with the Apple training app on the iPhone, it also looks like there is no connected HKWorkoutRoute data stored.
I also can not find any metadata key or something which I can use instead.
Post not yet marked as solved
I created some objects and pusehd them to core spotlight using the CSSearchableIndex.default().indexSearchableItems function.Now I also wanted the app to run on macOS, so I enabled the Mac target in my Deployment Info.So far so good. The objects also show up in the spotlight search on macOS.My question is: how do I set up the preview?The default is just an empty view and when I try to use my iOS "Quick Look Preview Extension" I get a compiler error:Quick Look Preview extensions are not available when building for Mac Catalyst.I also tried adding a macOS "Quick Look Preview Extension" target. But it also does not work. I guess it is not working, because I do not have a macOS app. What is the correct way to provide the preview on mac catalyst?
Post not yet marked as solved
I have a NSFetchedResultsController combined with a UITableViewDiffableDataSource.There is a bool attribute "marked" in my entity which I want to change by clicking on the table view cell. To display the status, I change the background color of the cell. However, the reload is not working as expected. The background color of the cell won't change.I first tried UITableViewDiffableDataSource<String, NSManagedObjectID>, but obviously that won't work for changes, because the NSManagedObjectID doesn't change. I then updated it to UITableViewDiffableDataSource<Int, Test>, but that also didn't work as expected.Any ideas how to solve this? Or is it just not (yet) possible?import UIKit
import CoreData
class MainViewController: UITableViewController {
lazy var fetchedResultController: NSFetchedResultsController<Test> = {
let fetchRequest: NSFetchRequest<Test> = Test.fetchRequest()
fetchRequest.sortDescriptors = [NSSortDescriptor(key: "timestamp", ascending: true)]
let fetchedResultController = NSFetchedResultsController<Test>(fetchRequest: fetchRequest, managedObjectContext: self.viewContext, sectionNameKeyPath: nil, cacheName: nil)
fetchedResultController.delegate = self
return fetchedResultController
}()
var persistentContainer: NSPersistentContainer { get { return (UIApplication.shared.delegate as! AppDelegate).persistentContainer } }
var viewContext: NSManagedObjectContext { get { return self.persistentContainer.viewContext } }
lazy var dataSource: UITableViewDiffableDataSource<Int, Test> = {
return UITableViewDiffableDataSource<Int, Test>(tableView: self.tableView) { (tableView, indexPath, cdObject) -> UITableViewCell? in
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = "\(cdObject.name!) (\(cdObject.id))"
cell.detailTextLabel?.text = DateFormatter.localizedString(from: cdObject.timestamp!, dateStyle: .short, timeStyle: .short)
cell.backgroundColor = cdObject.clicked ? UIColor.systemOrange : UIColor.systemBackground
return cell
}
}()
@IBAction func barButtonPressed_insert(_ sender: UIBarButtonItem) {
for i in 0 ..< 7 {
let newCDEntry = Test(context: self.viewContext)
newCDEntry.id = Int16(i)
newCDEntry.name = "Name \(i)"
newCDEntry.timestamp = Date()
}
self.viewContext.transactionAuthor = "regular insert"
defer { self.viewContext.transactionAuthor = nil }
do {
try self.viewContext.save()
} catch {
let nserror = error as NSError
if let conflicts = nserror.userInfo["conflictList"] as? [NSConstraintConflict] {
for conflict in conflicts {
for conflictObject in conflict.conflictingObjects {
self.viewContext.delete(conflictObject)
}
}
}
do {
try self.viewContext.save()
} catch {
print("##### error saving insert")
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
do {
try self.fetchedResultController.performFetch()
} catch {
print("could not fetch data")
}
self.applyFetchedDataToDataSource(animated: false)
}
private func applyFetchedDataToDataSource(animated: Bool) {
var snapshot = NSDiffableDataSourceSnapshot<Int, Test>()
snapshot.appendSections([0])
snapshot.appendItems(self.fetchedResultController.fetchedObjects ?? [])
self.dataSource.apply(snapshot, animatingDifferences: animated)
}
@objc private func managedObjectContextObjectDidSave(notification: Notification) {
print("received save notification")
self.viewContext.mergeChanges(fromContextDidSave: notification)
}
// MARK: - TableView
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: true)
let cdObject = self.fetchedResultController.object(at: indexPath)
cdObject.clicked = !cdObject.clicked
do {
self.viewContext.transactionAuthor = "click toggled"
defer { self.viewContext.transactionAuthor = nil }
try self.viewContext.save()
} catch {
print("could not save clicked toggle")
}
}
}
extension MainViewController: NSFetchedResultsControllerDelegate {
func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
self.applyFetchedDataToDataSource(animated: true)
}
}