Xcode 11.1, Swift 4
How to draw a line between three locations (points) using Mapkit in Swift?
let london = Capital(title: "London", coordinate: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), info: "Home to the 2012 Summer Olympics.")
let oslo = Capital(title: "Oslo", coordinate: CLLocationCoordinate2D(latitude: 59.95, longitude: 10.75), info: "Founded over a thousand years ago.")
Thanks
To connect any number of points you can use MKPolyline and its MKPolylineRenderer view. First you add the overlay to your map, then provide the view with visual settings for the polyline in the delegate method:
import UIKit
import MapKit
struct Capital {
let title: String
let coordinate: CLLocationCoordinate2D
let info: String
}
class ViewController: UIViewController, MKMapViewDelegate {
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
mapView.delegate = self
let london = Capital(title: "London", coordinate: CLLocationCoordinate2D(latitude: 51.507222, longitude: -0.1275), info: "Home to the 2012 Summer Olympics.")
let oslo = Capital(title: "Oslo", coordinate: CLLocationCoordinate2D(latitude: 59.95, longitude: 10.75), info: "Founded over a thousand years ago.")
mapView.addOverlay(MKPolyline(coordinates: [london.coordinate, oslo.coordinate], count: 2))
}
func mapView(_ mapView: MKMapView, rendererFor overlay: MKOverlay) -> MKOverlayRenderer {
let polylineRenderer = MKPolylineRenderer(polyline: overlay as! MKPolyline)
polylineRenderer.strokeColor = UIColor.black
polylineRenderer.lineWidth = 4.0
return polylineRenderer
}
}
Here is the result of the code above:
Related
Scenario: TabbedUI with Tab #1 being a WkWebView displaying a PDF of general information (Info).
Problem: When I exit tab #1 (info) for another tab (ex. search), and return....I get an empty PDF with the following error message in the console:
Could not signal service com.apple.WebKit.WebContent: 113: Could not
find specified service
However this doesn't happen when I use a standard .rtf (Rich Text Format) file.
Here's my code:
import SwiftUI
import WebKit
struct IntroSwiftUI: View {
var body: some View {
Webview()
.frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity)
.edgesIgnoringSafeArea(.bottom)
}
}
// =====================================================================================================
struct Webview: UIViewRepresentable {
func makeUIView(context: Context) -> WKWebView {
guard let url = Bundle.main.url(forResource: "ReadMe", withExtension: "pdf") else {
print("Inside Webview: unable to reload ReadMe.")
return WKWebView()
}
let request = URLRequest(url: url)
let wkWebview = WKWebView()
wkWebview.load(request)
wkWebview.scrollView.bounces = false
return wkWebview
}
func updateUIView(_ uiView: WKWebView, context: Context) {
}
}
I just want a static PDF to be available for viewing whenever the user which to read some documentation (with embedded images). But apparently it tries to rebuild and gets lost.
Do I need to do something 'special' with the PDF, like release it when exiting & re-create it upon return? That seems totally inefficient.
Per suggestion,
Use the PDFView paradigm.
Here is my revised (correct) code for PDF viewing:
import SwiftUI
import UIKit
import PDFKit
struct PDFKitRepresentedView: UIViewRepresentable {
let url: URL
init(_ url: URL) {
self.url = url
}
func makeUIView(context: UIViewRepresentableContext<PDFKitRepresentedView>) -> PDFKitRepresentedView.UIViewType {
// Create a `PDFView` and set its `PDFDocument`.
let pdfView = PDFView()
pdfView.document = PDFDocument(url: self.url)
return pdfView
}
func updateUIView(_ uiView: UIView, context: UIViewRepresentableContext<PDFKitRepresentedView>) {
// Update the view.
}
}
struct PDFKitView: View {
var url: URL
var body: some View {
PDFKitRepresentedView(url)
}
}
// =====================================================================================================
struct IntroSwiftUI: View {
var body: some View {
if let documentURL = Bundle.main.url(forResource: "ReadMe", withExtension: "pdf") {
VStack(alignment: .center) {
Text("Introduction")
.font(.title3)
.fontWeight(Font.Weight.medium)
PDFKitView(url: documentURL)
}
} else {
Text("Sorry, No PDF")
}
}
}
I was using the below function to fly the camera to required camera view in 3D view till now. But when I try this same function in Columbus view, the camera view is not as required.
goToLocation(-114.07201, 51.04357, 198.21, 43.20, 68.65, 1);
function goToLocation(longitude, latitude, altitude, heading, tilt, duration) {
var coords = Cesium.Cartesian3.fromDegrees(longitude, latitude, altitude, Cesium.Ellipsoid.WGS84);
var heading = Cesium.Math.toRadians(heading);
var tilt = Cesium.Math.toRadians(tilt - 90);
var camera_v = viewer.camera;
var options = {
destination: coords,
duration: duration,
orientation: {
heading: heading,
pitch: tilt,
roll: 0.0
}
};
camera_v.flyTo(options);
}
Can anyone please suggest any changes to be done to this function for columbus view?
My code is trying to download some JSON data and save it to an array, then loop through the array and create a button for each item. I am having trouble assigning my function to the buttons for giving them functionality. Here is my code:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
//connect to website
let SongArray: Array<Any>
let url = URL(string:"*******")
let task = URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error != nil
{
print("error")
}
else
{
if let content = data
{
do
{
//download JSON data from php page, display data
let SongArray = try JSONSerialization.jsonObject(with: content, options: JSONSerialization.ReadingOptions.mutableContainers) as! [[String]]
print(SongArray)
//Make buttons with JSON array
var buttonY: CGFloat = 20
for song in SongArray {
let SongButton = UIButton(frame: CGRect(x: 50, y: buttonY, width: 250, height: 30))
buttonY = buttonY + 50 // 50px spacing
SongButton.layer.cornerRadius = 10 //Edge formatting for buttons
SongButton.backgroundColor = UIColor.darkGray //Color for buttons
SongButton.setTitle("\(song[0])", for: UIControlState.normal) //button title
SongButton.titleLabel?.text = "\(song[0])"
SongButton.addTarget(self,action: #selector(songButtonPressed(_:)), for: UIControlEvents.touchUpInside) //button press / response
self.view.addSubview(SongButton) // adds buttons to view
}
}
catch
{
}
}
}
}
task.resume()
}
} //close viewDidLoad
func songButtonPressed(_sender:UIButton!) { // function for buttons
if sender.titleLabel?.text == "\("Song[0]")" {
print("So far so good!!")
}
}
I am getting an error on the line with SongButton.addTarget...
the error says 'Use of Unresolved Identifier "SongButtonPressed"' even though its declared right after the viewDidLoad function.
Since you declared the selector SongButtonPressed(_:) it's (note the underscore)
func SongButtonPressed(_ sender: UIButton) { // no implicit unwrapped optional !!
By the way the proper selector syntax is #selector(SongButtonPressed(_:))
Two notes:
.mutableContainers has no effect in Swift at all. Omit the parameter. And delete the line let SongArray: Array<Any>. You should get an unused warning.
Functions, methods and variables are supposed to start with a lowercase letter.
I am trying to implement the action from notification. And so far I am able to trigger the right delegate functions, but after tapping the app is not brought to the foreground.
Relevant code:
#available(iOS 10.0, *)
func registerCategory() -> Void{
print("register category")
let callNow = UNNotificationAction(identifier: "call", title: "Call now", options: [])
let clear = UNNotificationAction(identifier: "clear", title: "Clear", options: [])
let category : UNNotificationCategory = UNNotificationCategory.init(identifier: "IDENT123", actions: [callNow, clear], intentIdentifiers: [], options: [])
let center = UNUserNotificationCenter.currentNotificationCenter()
center.setNotificationCategories([category])
}
#available(iOS 10.0, *)
func scheduleNotification(event : String, interval: NSTimeInterval) {
print("schedule ", event)
let content = UNMutableNotificationContent()
content.title = event
content.body = "body"
content.categoryIdentifier = "CALLINNOTIFICATION"
let trigger = UNTimeIntervalNotificationTrigger.init(timeInterval: interval, repeats: false)
let identifier = "id_"+event
let request = UNNotificationRequest.init(identifier: identifier, content: content, trigger: trigger)
let center = UNUserNotificationCenter.currentNotificationCenter()
center.addNotificationRequest(request) { (error) in
}
}
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, willPresentNotification notification: UNNotification, withCompletionHandler completionHandler: (UNNotificationPresentationOptions) -> Void) {
print("willPresent")
completionHandler([.Badge, .Alert, .Sound])
}
#available(iOS 10.0, *)
func userNotificationCenter(center: UNUserNotificationCenter, didReceiveNotificationResponse response: UNNotificationResponse, withCompletionHandler completionHandler: () -> Void) {
let notification: UNNotification = response.notification
let UUID = notification.request.content.userInfo["UUID"] as! String
switch (response.actionIdentifier) {
case "COMPLETE":
UNUserNotificationCenter.currentNotificationCenter().removeDeliveredNotificationsWithIdentifiers([UUID])
case "CALLIN":
let call = Call()
CalendarController.sharedInstance.fetchMeetingByUUID(UUID, completion: { (thisMeeting) -> Void in
if(!CallIn.Yield(thisMeeting).ConferenceCallNumber.containsString("None")){
call._call(thisMeeting)
}else{
//will open detail view, in case that no number were detected
NSNotificationCenter.defaultCenter().postNotificationName("OpenDetailViewOfMeeting", object: self, userInfo: ["UUID":UUID])
}
})
UNUserNotificationCenter.currentNotificationCenter().removeDeliveredNotificationsWithIdentifiers([UUID])
default: // switch statements must be exhaustive - this condition should never be met
log.error("Error: unexpected notification action identifier: \(UUID)")
}
completionHandler()
}
I am able to hit the delegate function didReceiveNotificationResponse() with a breakpoint, and it does some actions that I put there, but not in a way that is expected (It has to start a device-call, instead it just dismisses notifications list, and nothing happens, however when I manually open the app again, the call starts as if there is no permission to open the app from notification).
I found out the reason myself, so this might be helpful to someone in the future. The answer turned out to be quite simple. When creating an action of the notification, there is this parameter: options. When you register category, you need to put it either way .Foreground or .Destructive like this:
func reisterCategory () {
let callNow = UNNotificationAction(identifier: NotificationActions.callNow.rawValue, title: "Call now", options: UNNotificationActionOptions.Foreground)
let clear = UNNotificationAction(identifier: NotificationActions.clear.rawValue, title: "Clear", options: UNNotificationActionOptions.Destructive)
let category = UNNotificationCategory.init(identifier: "NOTIFICATION", actions: [callNow, clear], intentIdentifiers: [], options: [])
let center = UNUserNotificationCenter.currentNotificationCenter()
center.setNotificationCategories([category])
}
In iOS10 User notifications were reworked by apple.
Now I am trying to adjust my app to these changes, following this:
let center = UNUserNotificationCenter.current()
gives me an error:
Type UNUserNotificationCenter has no member current, swift 2.3
I know that that tutorial is maybe for swift3. But I need to make it work in swift 2.3. Is it even feasible and if yes, how to do it?
for Xcode 8 / ios9/10
simply use:
import UserNotifications
.
.
// swift 3.0:
if #available(iOS 10.0, *) {
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.alert, .sound]) { (granted, error) in
// Enable or disable features based on authorization.
}
} else {
// Fallback on earlier versions
}
// NOTE: if are in FOREGROUND, You will NEVER be called! :)
The documentation seems in conflict with itself. Although it describes the current() method and says to use it, the examples show let center = UNUserNotificationCenter.currentNotificationCenter().
As you say, this may be a Swift version dependency.
Import this:
import UserNotifications
implement this delegate with in your appDelegate file:
UNUserNotificationCenterDelegate
Which will give you access to the following methods:
func userNotificationCenter(_ center: UNUserNotificationCenter, openSettingsFor notification: UNNotification?) {}
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: #escaping () -> Void) {}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: #escaping (UNNotificationPresentationOptions) -> Void) {}
and then add this to your app delegate's didFinishLaunchWithOptions method to tie it all up.
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
swift 4.1
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().delegate = self as? UNUserNotificationCenterDelegate
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {_, _ in
self.getNotificationSettings()
})
} else {
// Fallback on earlier versions
let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
UIApplication.shared.registerUserNotificationSettings(settings)
}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}