I'm trying to make an Apple Watch app (extension) which shows a map in a way similar to Apple's Maps app. I want to be able to show a portion of the map, and then be able to use the digital crown to zoom and gestures to scroll.
I think this should be possible with Watch OS2, but I haven't figured out which APIs to use.
Is this possible on the platform at present?
Update: I don't think I can use WKInterfaceMap since I want to generate my own map, not use an existing one.
Inside Watch OS2 API only WKInterfacePicker has access of Digit Crown
So We can't use Digit Crown For WKInterfaceMap Directly but we can use it by using WKInterfacePicker
Put WKInterfaceMap and WKInterfacePicker inside Group
Set WKInterfacePicker size width fixed = 2 height fixed = 2
(We can not set size to zero)
NOTE : Don't hide WKInterfacePicker, Sometime, It resign focus because of hidden .
Add below code for Picker Controller
code
class InterfaceController: WKInterfaceController {
#IBOutlet var mapView : WKInterfaceMap!
#IBOutlet var tempPicker:WKInterfacePicker!
var zoomArray:[Double] = [1.0,0.98,0.96,0.94,0.92,0.90,0.88,0.86,0.84,0.82,0.80,0.78,0.76,0.74,0.72,0.70,0.68,0.66,0.64,0.62,0.60,0.58,0.56,0.54,0.52,0.50,0.48,0.46,0.44,0.42,0.40,0.38,0.36,0.34,0.32,0.30,0.28,0.26,0.24,0.22,0.20,0.18,0.16,0.14,0.12,0.10,0.09,0.08,0.07,0.06,0.05,0.04,0.03,0.02,0.01,0.009,0.008,0.007,0.006,0.005,0.004,0.003,0.002,0.001]
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
let pickerItems: [WKPickerItem] = zoomArray.map {
let pickerItem = WKPickerItem()
pickerItem.caption = String($0)
pickerItem.title = String($0)
return pickerItem
}
self.tempPicker.setItems(pickerItems)
self.tempPicker.setSelectedItemIndex(25)
let span = MKCoordinateSpanMake(zoomArray[25], zoomArray[25])
let lc = watchDelegate.locationModel.myLocation ?? CLLocationCoordinate2D.init(latitude: 0.0, longitude: 0.0)//watchDelegate.locationModel.myLocation is current user Location
let region = MKCoordinateRegionMake(lc, span)
self.mapView.setRegion(region)
}
#IBAction func pickerChanged(value: Int) {
let lc = watchDelegate.locationModel.myLocation ?? CLLocationCoordinate2D.init(latitude: 0.0, longitude: 0.0)
let span = MKCoordinateSpanMake(zoomArray[value], zoomArray[value])
let region = MKCoordinateRegionMake(lc,
span)
self.mapView.setRegion(region)
self.tempPicker.focus()
}
}
Connect #IBAction and IBOutlet to WKInterfacePicker and WKInterfaceMap
Related
I'm trying to build a widget that has a gauge like in the image attached. It does not seem like there are any APIs to render the gauge (or any other view for that matter) on an arc, depending on which corner is used.
Is there any such support, or are such widgets only available to Apple? E.g. can one tell which corner the widget is being rendered in, so that the correct transformations be computed?
Thank you!
You can get close to an Apple style corner widget, but there are currently some limitations. As far as I know you have to use the .widgetLabel modifier which restricts you to an "Image, Text, Gauge, ProgressView, or a container with multiple subviews".
The styling for the Gauge and ProgressView seem to be predefined as well - for example styling the gauge with .gaugeStyle(LinearCapacityGaugeStyle()) doesn't change the appearance.
var body: some View {
switch widgetFamily {
case .accessoryCorner: // WatchOS only
Text("50%") // Watch out for clipping
.font(.system(size: 20))
.foregroundColor(.blue)
.widgetLabel {
ProgressView(value: 0.5)
.tint(.blue)
}
}
}
or
var body: some View {
switch widgetFamily {
case .accessoryCorner: // WatchOS only
Text("50%") // Watch out for clipping
.font(.system(size: 20))
.foregroundColor(.blue)
.widgetLabel {
Gauge(value: 50.0, in: 0...100) {
Text("Not shown")
} currentValueLabel: {
Text("Not shown")
} minimumValueLabel: {
Text("0") // Watch out for clipping
} maximumValueLabel: {
Text("100") // Watch out for clipping
}
.tint(.blue)
.gaugeStyle(LinearCapacityGaugeStyle()) // Doesn't do anything
}
Gives you:
You can rotate the text manually to try and make it line up with corner, but as you say then there doesn't seem to be a way to identify which corner the widget is in so you don't know which way to rotate it...
I'm working on a swiftUI app where I have to display multiple PDF files in one screen.
I've created a PDFView:
struct PDFKitRepresentedView: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
let pdfView = PDFView()
let pdfDocument = PDFDocument(url: self.url)
pdfView.document = pdfDocument
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
// Update the view.
}
}
struct PDFKitView: View {
var url: URL
var body: some View {
PDFKitRepresentedView(url)
}
}
The PDF is created here:
if let url = attachment.path {
PDFKitView(url: url)
.frame(width: UIScreen.screenWidth - 40, height: UIScreen.screenHeight - 40, alignment: .center)
.padding()
}
The problem I'm having is that, whenever the first document is multipage, in order to see the other documents the user has first to pinch to zoom out completely and the other PDF's are shown in sequence.
I've tried to add this values, but that just makes the content of the PDF to disappear
I was wondering if setting the frame on the PDFKitView directly could be causing the issue, but no.
Anyone has any suggestions on how to make this work? I assume that if I could make the pdf to show already with a min zoom it would display the view correctly.
I finally found the answer on this post How to detect where NaN is passing to CoreGraphics API on Mac OS X 10.9
3D object place perfectly in ARSCNView but problem is that when object placed in AR and move camera right, left, top and bottom too fast then 3D object started hovering and dancing anywhere with the planeNode
how I can fix this issue, trying lots of way to find the solution still not get any result
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor) {
// Cast ARAnchor as ARPlaneAnchor
guard let planeAnchor = anchor as? ARPlaneAnchor else { return }
let planeGeometry = ARSCNPlaneGeometry(device: MTLCreateSystemDefaultDevice()!)!
planeGeometry.update(from: planeAnchor.geometry)
// Add material to geometry
let material = SCNMaterial()
material.diffuse.contents = UIColor.blue.withAlphaComponent(0.8)
planeGeometry.materials = [material]
// Create a SCNNode from geometry
let planeNode = SCNNode(geometry: planeGeometry)
self.privateNode = planeNode
self.anchors.append(planeNode)
DispatchQueue.main.async {
self.lbl_middle_heading.text = Constants.kSharedAppDelegate?.languageBundle.localizedString(forKey: "Please tap anywhere on screen.", value: "", table: nil)
self.showFeaturePoints(isShowDeugOptions: false)
}
// Add the newly created plane node as a child of the node created for the ARAnchor
node.addChildNode(planeNode)
}
func renderer(_ renderer: SCNSceneRenderer, didUpdate node: SCNNode, for anchor: ARAnchor) {
// Cast ARAnchor as ARPlaneAnchor, get the child node of the anchor, and cast that node's geometry as an ARSCNPlaneGeometry
guard
let planeAnchor = anchor as? ARPlaneAnchor,
let planeNode = node.childNodes.first,
let planeGeometry = planeNode.geometry as? ARSCNPlaneGeometry
else { return }
planeGeometry.update(from: planeAnchor.geometry)
}
The model is "dancing" or drifting away along some axis in AR app for three main reasons:
if you imported an animated model
if a model isn't tethered by ARAnchor
if a tracking of your scene is poor
Track a surrounding environment thoroughly and make sure it's accordingly lit and has quite detailed texture for getting a sufficient quantity of feature points to track.
How do I automatically get the bounds of the current map view? I've been looking everywhere. The Google Maps API has it: http://www.w3schools.com/googleapi/ref_getbounds.asp
It would be nice to do it for React Native
If you want it then you can have one extension for the MKMapView as below.
extension MKMapView {
func getBounds() -> (southWestPoint: CLLocationCoordinate2D, northEastPoint: CLLocationCoordinate2D) {
let visibleMapRect = self.visibleMapRect
let neMapPoint = MKMapPointMake(MKMapRectGetMaxX(visibleMapRect), visibleMapRect.origin.y)
let swMapPoint = MKMapPointMake(visibleMapRect.origin.x, MKMapRectGetMaxY(visibleMapRect))
return (southWestPoint: MKCoordinateForMapPoint(swMapPoint), northEastPoint:MKCoordinateForMapPoint(neMapPoint))
}
}
Whenever you want the bound just call the getBounds method.
So this is kind of a noobish question but I just can't figure out a very simple way to detect currently focused item's indexPath.
I looked around hoping to see something very easy like collectionView.indexPathOfCurrentlyFocusedItem but didn't find anything remotely close.
So I digged around and tried to find something similar at UIFocusEnvironment, UIFocusUpdateContext trying to find the desired property but failed.
So, the only solution I can come up with is just iterating through all visible cells and finding a cell with focused property set to true.
So is there a more simple and elegant way to find the currently focused item's indexPath? (Except tracking it through delegate method and saving it in view controller's property)
You can use UIScreen property focusedView as followed for this:
if let focusedCell = UIScreen.main.focusedView as? UICollectionViewCell {
if let indexPath = collectionView.indexPath(for: focusedCell) {
print("IndexPath is \(indexPath)")
}
}
Use didUpdateFocusInContect - UICollectionViewDelegate
func collectionView(collectionView: UICollectionView, didUpdateFocusInContext context: UICollectionViewFocusUpdateContext, withAnimationCoordinator coordinator: UIFocusAnimationCoordinator) {
if collectionView == self.collectionView {
print(context.nextFocusedIndexPath)
}
}
This wil return the indexPath of the cell that is going to be focused, you could also try:
context.previouslyFocusedIndexPath
Depends what you're trying to do.
So, your target is to do something when you get and lose focus particularly on a cell under tvOS. The catch is you're moving around other UI elements, and therefore the context could be different. You have to change in this context only those UIs that you have to care of.
The right place to make your implementation is func didUpdateFocusInContext(), like this:
override func didUpdateFocusInContext(
context: UIFocusUpdateContext,
withAnimationCoordinator coordinator: UIFocusAnimationCoordinator
) {
coordinator.addCoordinatedAnimations({
if let cell = context.previouslyFocusedView as? UICollectionViewCell {
cell.layer.borderWidth = 2
}
if let cell = context.nextFocusedView as? UICollectionViewCell {
cell.layer.borderWidth = 5
}
},
completion: nil)
}
Now we're using the focus coordinator to apply our logic:
When the previously focused item is UICollectionViewCell then you have to release the focus to the next item. You shouldn't care what is the next item because it could be a collection cell or not. For fun, in this case, let's change the border to 2. This value could be set by default.
When the next focused item is UICollectionViewCell then you've to handle it is a similar way, or it will become a mess... So, let's change the border to 5.
As you can see, didUpdateFocusInContext() provides a generic approach for all views within your current visual context. You can apply the same approach for other UI elements.
Have a fun with tvOS...
Here's how I accomplished this in shouldUpdateFocusInContext
Solution
override func shouldUpdateFocusInContext(context: UIFocusUpdateContext) -> Bool {
// The magic is in the next two lines
let cell: UICollectionViewCell = context.nextFocusedView as! UICollectionViewCell
let indexPath: NSIndexPath? = self.collectionView.indexPathForCell(cell)
print(indexPath)
// <NSIndexPath: 0xc000000000000016> {length = 2, path = 0 - 0}
return true
}