UITapGestureRecognizer Error - swift3

I have been struggling with this issue for a few hours. I can't seem to understand the procedure of using a UITapGestureRecognizer. Any help would be appreciated.
#IBOutlet weak var textView: UITextView!
override func viewDidLoad() {
let textInView = "This is my text."
textView.text = textInView
let tapGesture = UITapGestureRecognizer(target: self, action: #selector(tapResponse(_:)))
tapGesture.numberOfTapsRequired = 1
textView.addGestureRecognizer(tapGesture)
func tapResponse(sender: UITapGestureRecognizer) {
var location: CGPoint = sender.location(in: textView)
location.x = textView.textContainerInset.left
location.y = textView.textContainerInset.top
print(location.x)
print(location.y)
}
}

You have a function (tapResponse) inside a function (viewDidLoad).
Put it outside of viewDidLoad. Then reference it like so:
override func viewDidLoad() {
super.viewDidLoad() // Remember to always call super.
let tapGesture = UITapGestureRecognizer(
target: self,
action: #selector(ViewController.tapResponse(sender:)) // Referencing.
)
tapGesture.numberOfTapsRequired = 1
textView.addGestureRecognizer(tapGesture)
}
// tapResponse is now outside of viewDidLoad:
func tapResponse(sender: UITapGestureRecognizer) {
var location: CGPoint = sender.location(in: imageView)
location.x = textView.textContainerInset.left
location.y = textView.textContainerInset.top
print(location.x)
print(location.y)
}
Done:

Related

SwiftUI: Highlighting Text with different colors using WkWebView

I am trying to use swiftUI to be able to create a custom EPUB reader. Ive looked around at some but none fit my needs. I want to be able to customize it. The issue I have ran into is being able to Highlight text while reading either Orange, blue, green, etc. When highlight a text and then the menu bar pops up and I click on my custom MenuBar color the app crashes. I found this article on highlighting text but uses the UIkit and not SwiftUI. Ive been trying to "translate"(not sure what the correct term is)it to use it with SwiftUI but crashes due to unrecognized selector.Im thinking I am not setting up the things correct. Not sure if it is worth using SwiftUI anymore and just switching my app to UIKit at this point since I have not been able to find many resources using swiftUI. Here is article to highlight text : https://dailong.medium.com/highlight-text-in-wkwebview-1659a19715e6
Just started learning swiftUI so not sure if the way the WebView is setup is correct.
Here is the gitHub link to all of the code https://github.com/longvudai/demo/tree/master/highlight-webview/highlight-webview With the SwiftUI all i did was copy and paste the files. Only difference is with SwiftUI was wrapping the WebView other than that everything else is the same.
SWIFTUI
`struct WebView: UIViewRepresentable {
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
var webView: CustomView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
func webView(_ webView: WKWebView?, didFinish navigation: WKNavigation!) {
self.webView = webView as? CustomView
}
// receive message from wkwebview
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
self.webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
func highlight(_ color: MarkerColor) {
let script =
MarkerScript.Evaluate.highlightSelectedTextWithColor(color)
webView?.evaluateJavaScript(script)
print("highlightfunction")
}
func removeAll() {
let script = MarkerScript.Evaluate.removeAllHighlights()
self.webView?.evaluateJavaScript(script)
dataStack.push([])
}
func erase() {
let script = MarkerScript.Evaluate.erase()
self.webView?.evaluateJavaScript(script)
}
#objc func highlightthiscolor() {
highlight(MarkerColor.orange)
}
}
func makeCoordinator() -> Coordinator {
return Coordinator()
}
func makeUIView(context: Context) -> CustomView {
let coordinator = makeCoordinator()
let configuration = WKWebViewConfiguration()
let uc = configuration.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(coordinator, name: MarkerScript.Handler.serialize.rawValue)
uc.add(coordinator, name: MarkerScript.Handler.erase.rawValue)
let _wkwebview = CustomView(frame: .zero, configuration: configuration)
_wkwebview.navigationDelegate = coordinator
return _wkwebview
}
func updateUIView(_ webView: CustomView, context: Context) {
guard let path: String = Bundle.main.path(forResource: "sample", ofType: "html") else { return }
let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
addCustomContextMenu()
}
func addCustomContextMenu(){
//Has to be type of WKWebView
let colorOrange:UIMenuItem = UIMenuItem(title: "Orange", action: #selector(Coordinator.highlightthiscolor))
UIMenuController.shared.menuItems = [colorOrange]
}
}`
UIKit
protocol MarkerLogic {
func erase()
func highlight(_ color: MarkerColor)
func removeAll()
}
class Marker: NSObject {
weak var webView: WKWebView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
}
extension Marker: MarkerLogic {
func highlight(_ color: MarkerColor) {
let script =
MarkerScript.Evaluate.highlightSelectedTextWithColor(color)
webView?.evaluateJavaScript(script)
}
func removeAll() {
let script = MarkerScript.Evaluate.removeAllHighlights()
webView?.evaluateJavaScript(script)
dataStack.push([])
}
func erase() {
let script = MarkerScript.Evaluate.erase()
webView?.evaluateJavaScript(script)
}
}
// MARK: - WKScriptMessageHandler
extension Marker: WKScriptMessageHandler {
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
}
--- ViewDidLoad
class ViewController: UIViewController, WKScriptMessageHandler {
let marker: Marker = Marker()
let orangeButton: UIButton = {
let v = UIButton()
v.tag = 0
v.backgroundColor = MarkerColor.orange.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let cyanButton: UIButton = {
let v = UIButton()
v.tag = 1
v.backgroundColor = MarkerColor.cyan.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let pinkButton: UIButton = {
let v = UIButton()
v.tag = 2
v.backgroundColor = MarkerColor.pink.value
v.layer.cornerRadius = 10
v.addTarget(self, action: #selector(highlight(_:)), for: .touchUpInside)
return v
}()
let eraseButton: UIButton = {
let v = UIButton()
v.setTitle("Erase", for: .normal)
v.setTitleColor(.systemBlue, for: .normal)
v.addTarget(self, action: #selector(erase), for: .touchUpInside)
return v
}()
let eraseAllButton: UIButton = {
let v = UIButton(type: .close)
v.addTarget(self, action: #selector(eraseAll), for: .touchUpInside)
return v
}()
lazy var toolBars: UIStackView = {
let v = UIStackView(arrangedSubviews: [orangeButton, cyanButton, pinkButton, eraseButton, eraseAllButton])
v.axis = .horizontal
v.distribution = .fillEqually
v.spacing = 20
return v
}()
// This is to make the makeUIView
lazy var webView: WKWebView = {
let config = WKWebViewConfiguration()
let uc = config.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(self.marker, name: MarkerScript.Handler.serialize.rawValue)
uc.add(self.marker, name: MarkerScript.Handler.erase.rawValue)
let v = WKWebView(frame: .zero, configuration: config)
return v
}()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
marker.webView = webView
let path = Bundle.main.path(forResource: "sample", ofType: "html")!
let url = URL(fileURLWithPath: path)
webView.loadFileURL(url, allowingReadAccessTo: url)
let views = [webView, toolBars]
views.forEach {
view.addSubview($0)
$0.translatesAutoresizingMaskIntoConstraints = false
}
NSLayoutConstraint.activate([
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor, constant: 40),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
toolBars.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
toolBars.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
toolBars.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
toolBars.heightAnchor.constraint(equalToConstant: 40)
])
}
// MARK: - Selector
#objc func highlight(_ sender: UIButton) {
switch sender.tag {
case 0:
marker.highlight(MarkerColor.orange)
case 1:
marker.highlight(MarkerColor.cyan)
case 2:
marker.highlight(MarkerColor.pink)
default:
break
}
}
#objc func erase() {
marker.erase()
}
#objc func eraseAll() {
marker.removeAll()
}
// MARK: - WKScriptMessageHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
}
}
I was finally able to get the code working by looking at the post: Call evaluateJavascript from a SwiftUI button. The problem I was running into was the fact that I was not able to run the javascript func in order to highlight. Using Combine i was able to make a button in the View and when that button is clicked being able to run the javascript code. Will post the code below for anyone who is interested.
import WebKit
import SwiftUI
import Combine
class WebViewData: ObservableObject {
#Published var parsedText: NSAttributedString? = nil
var functionCaller = PassthroughSubject<Void,Never>()
var isInit = false
var shouldUpdateView = true
}
struct ContentView: View {
#StateObject var webViewData = WebViewData()
var body: some View {
VStack {
Button(action: {
webViewData.functionCaller.send()
}) {
Text("Orange")
}
WebView(data: webViewData)
}
}
}
struct WebView: UIViewRepresentable {
#StateObject var data: WebViewData
class Coordinator: NSObject, WKNavigationDelegate, WKScriptMessageHandler {
//var webView: WKWebView?
var serializedObject: SerializedObject?
private var dataStack = Stack<Highlights>()
var parent: WebView
var webView: WKWebView? = nil
private var cancellable : AnyCancellable?
init(view: WebView) {
self.parent = view
super.init()
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
self.webView = webView
}
// receive message from wkwebview
func userContentController(
_ userContentController: WKUserContentController,
didReceive message: WKScriptMessage
) {
if let markerHandler = MarkerScript.Handler(message) {
guard
let dataString = message.body as? String,
let data = dataString.data(using: .utf8)
else { return }
let decoder = JSONDecoder()
guard let serialized = try? decoder.decode(
SerializedObject.self,
from: data
) else { return }
receiveMarkerMessage(markerHandler, data: serialized)
}
}
func receiveMarkerMessage(_ handler: MarkerScript.Handler, data: SerializedObject) {
switch handler {
case .serialize:
serializedObject = data
// your callback here
let script = MarkerScript.Evaluate.clearSelection()
self.webView?.evaluateJavaScript(script)
case .erase:
serializedObject = data
let highlights = data.highlights
let listId = highlights.map { $0.id }
guard let top = dataStack.top else { return }
let newData = top.filter { listId.contains($0.id) }
if newData != top {
dataStack.push(newData)
}
}
}
func tieFunctionCaller(data: WebViewData) {
cancellable = data.functionCaller.sink(receiveValue: { _ in
self.webView?.evaluateJavaScript("highlightSelectedTextWithColor('orange')")
})
}
}
func makeCoordinator() -> Coordinator {
return Coordinator(view: self)
}
func makeUIView(context: Context) -> WKWebView {
let coordinator = makeCoordinator()
let configuration = WKWebViewConfiguration()
let uc = configuration.userContentController
uc.addUserScript(WKUserScript.injectViewPort())
// Jquery
uc.addUserScript(JQueryScript.core())
// Rangy
uc.addUserScript(RangyScript.core())
uc.addUserScript(RangyScript.classapplier())
uc.addUserScript(RangyScript.highlighter())
uc.addUserScript(RangyScript.selectionsaverestore())
uc.addUserScript(RangyScript.textrange())
// Marker
uc.addUserScript(MarkerScript.css())
uc.addUserScript(MarkerScript.jsScript())
uc.add(coordinator, name: MarkerScript.Handler.serialize.rawValue)
uc.add(coordinator, name: MarkerScript.Handler.erase.rawValue)
let _wkwebview = WKWebView(frame: .zero, configuration: configuration)
_wkwebview.navigationDelegate = coordinator
return _wkwebview
}
func updateUIView(_ webView: WKWebView, context: Context) {
guard data.shouldUpdateView else {
data.shouldUpdateView = false
return
}
context.coordinator.tieFunctionCaller(data: data)
context.coordinator.webView = webView
guard let path: String = Bundle.main.path(forResource: "sample", ofType: "html") else { return }
let localHTMLUrl = URL(fileURLWithPath: path, isDirectory: false)
webView.loadFileURL(localHTMLUrl, allowingReadAccessTo: localHTMLUrl)
}
}

How can I have a UIButton (UIViewControllerRepresentable) trigger an action in my model?

I wish to have a UIButton in a UIViewController run a function in my ObservableObject in my SwiftUI app. My needs are larger than this, so I've trimmed out things to this code:
UIViewController & Delegate
protocol TestVCDelegate {
func runTest()
}
class TestVC: UIViewController {
let btnTest = UIButton()
let lblTest = UILabel()
var delegate:TestVCDelegate! = nil
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.red
btnTest.translatesAutoresizingMaskIntoConstraints = false
lblTest.translatesAutoresizingMaskIntoConstraints = false
btnTest.backgroundColor = UIColor.green
btnTest.addTarget(self, action: #selector(runTest), for: .touchUpInside)
view.addSubview(btnTest)
btnTest.heightAnchor.constraint(equalToConstant: 100).isActive = true
btnTest.widthAnchor.constraint(equalToConstant: 100).isActive = true
btnTest.topAnchor.constraint(equalTo: view.topAnchor, constant: 100).isActive = true
btnTest.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 100).isActive = true
view.addSubview(lblTest)
lblTest.heightAnchor.constraint(equalToConstant: 100).isActive = true
lblTest.widthAnchor.constraint(equalToConstant: 100).isActive = true
lblTest.topAnchor.constraint(equalTo: btnTest.bottomAnchor, constant: 10).isActive = true
lblTest.leadingAnchor.constraint(equalTo: self.view.leadingAnchor, constant: 100).isActive = true
}
#objc func runTest() {
delegate.runTest()
lblTest.text = "hello world!"
}
}
UIViewControllerRepresentable & Coordinator
struct Take2: UIViewControllerRepresentable {
#EnvironmentObject var model: Model
let testVC = TestVC()
func makeUIViewController(context: Context) -> TestVC {
testVC.delegate = context.coordinator
return testVC
}
func updateUIViewController(_ uiViewController: TestVC, context: Context) {
//
}
func makeCoordinator() -> Coordinator {
Coordinator(self)
}
class Coordinator: NSObject, TestVCDelegate {
var parent: Take2
init(_ testViewController: Take2) {
parent = testViewController
}
func runTest() {
print("hello world")
}
}
}
Model
class Model : ObservableObject {
var objectWillChange = PassthroughSubject<Void, Never>()
#Published var lblText = "" {
willSet {
objectWillChange.send()
}
}
func runTest() {
print("yet again, hello world")
lblText = "hello world!"
}
}
Every time I try to execute runTest() in my model I get this error:
Fatal error: Reading EnvironmentObject outside View.body
Now, this error happens whether I try to do this in Take2, it's Coordinator, or in my TestVC. I (mostly) understand the error - if I should try to execute this in some View via onAppear it works. BUT - I thought a UIViewControllerRepresentable is a View to the SwiftUI stack (even though it isn't some View).
I've succeeded in exposing UIKit properties (both get and set) to the SwiftUI stack. I also have UIKit delegates (specifically, UIimagepickerController) delegate successfully updating SwiftUI properties.
But how can I execute a function in an ObservableObject from a UIKit UIButton?

UserDefaults.standard not saving swift3 xcode 8

Hello everybody I'm trying to build a simple one view program to call and navigate a voice menu, I cannot get the UserDefaults to properly save between closing of the app. I've also had issue with getting text to display in the UITextField, I would like for it to display if saved on launch, here is a description of vars:
UserInput - text field for employee ID (what needs saving)
Switch - if Save it is 1 save data, if 0 do not
Submit - submit button that should save the data and call.
This is my first iPhone app and any help would be much appreciated!
enter code here
import UIKit
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var UserInput: UITextField!
var SaveIt = true
var employID = ""
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
self.UserInput.delegate = self;
UserInput.keyboardType = .numberPad
if let current_eid = UserDefaults.standard.string(forKey: "emp_ID") {
UserInput.text = current_eid
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func loadDefaults() {
let defaults = UserDefaults.standard
if let UserInput = defaults.string(forKey: "emp_ID") {
print(UserInput)
} else {
// focus on the text field if it's empty
UserInput.becomeFirstResponder() }
UserInput.text = defaults.object(forKey: "emp_ID") as! String?
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
#IBAction func Switch(_ sender: UISwitch) {
if (sender.isOn == true) {
SaveIt = true
}
else{
SaveIt = false
}
}
#IBAction func Submit(_ sender: Any) {
if (SaveIt) {
let defaults = UserDefaults.standard
defaults.set(UserInput.text, forKey: "emp_ID")
defaults.synchronize()
}
let phone = "telprompt://1888247889pppp" + UserInput.text! + "p1p1p1p1";
let url = URL(string:phone)!
if #available(iOS 10.0, *) {
UIApplication.shared.open(url, options: [:], completionHandler: nil)
} else { UIApplication.shared.openURL(url)
}
}
}

MapView in a VC and its Searchbar in another

As per the title, in general I'd like to show, in one VC, a MapView and in a second VC a SearchBar (using another TableVC to display, as the user types, "GeoElements" obtained by MKLocalSearchRequest). This because I want to display in the Map some AnnotationViews with custom Callouts. My problem is that the TableVC doesn't update with placemarks as the user types in the SearchBar..
MAPVIEW code:
import UIKit
import MapKit
protocol HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark)
}
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationManager = CLLocationManager()
var selectedPin:MKPlacemark? = nil
override func viewDidLoad() {
super.viewDidLoad()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyThreeKilometers
locationManager.requestWhenInUseAuthorization()
locationManager.requestLocation()
}
func getDirections(){
if let selectedPin = selectedPin {
let mapItem = MKMapItem(placemark: selectedPin)
let launchOptions = [MKLaunchOptionsDirectionsModeKey : MKLaunchOptionsDirectionsModeDriving]
mapItem.openInMaps(launchOptions: launchOptions)
}
}
}
extension ViewController : CLLocationManagerDelegate {
private func locationManager(manager: CLLocationManager, didChangeAuthorizationStatus status: CLAuthorizationStatus) {
if status == .authorizedWhenInUse {
locationManager.requestLocation()
}
}
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
if let location = locations.first {
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegion(center: location.coordinate, span: span)
mapView.setRegion(region, animated: true)
}
}
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
print("error:: (error)")
}
}
extension ViewController: HandleMapSearch {
func dropPinZoomIn(placemark:MKPlacemark){
// cache the pin
selectedPin = placemark
let annotation = MKPointAnnotation()
annotation.coordinate = placemark.coordinate
annotation.title = placemark.name
if let city = placemark.locality,
let state = placemark.administrativeArea {
annotation.subtitle = "(city) (state)"
}
mapView.addAnnotation(annotation)
let span = MKCoordinateSpanMake(0.05, 0.05)
let region = MKCoordinateRegionMake(placemark.coordinate, span)
mapView.setRegion(region, animated: true)
}
}
extension ViewController : MKMapViewDelegate {
func mapView(_: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView?{
if annotation is MKUserLocation {
//return nil so map view draws "blue dot" for standard user location
return nil
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationView(withIdentifier: reuseId) as? MKPinAnnotationView
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId)
pinView?.pinTintColor = UIColor.orange
pinView?.canShowCallout = true
let smallSquare = CGSize(width: 30, height: 30)
let button = UIButton(frame: CGRect(origin: CGPoint(x: 0,y :0), size: smallSquare))
button.setBackgroundImage(UIImage(named: "car"), for: .normal)
button.addTarget(self, action: Selector(("getDirections")), for: .touchUpInside)
pinView?.leftCalloutAccessoryView = button
return pinView
}
}
EVENT code:
import UIKit
import MapKit
class EventVC: UIViewController {
var resultSearchController:UISearchController? = nil
override func viewDidLoad() {
super.viewDidLoad()
let locationSearchTable = storyboard!.instantiateViewController(withIdentifier: "LocationSearchTable") as! LocationSearchTable
resultSearchController = UISearchController(searchResultsController: locationSearchTable)
resultSearchController?.searchResultsUpdater = locationSearchTable
let searchBar = resultSearchController!.searchBar
searchBar.sizeToFit()
searchBar.placeholder = "Insert Address"
navigationItem.titleView = resultSearchController?.searchBar
resultSearchController?.hidesNavigationBarDuringPresentation = false
resultSearchController?.dimsBackgroundDuringPresentation = true
definesPresentationContext = true
//these two below are problematic
locationSearchTable.mapView = mapView
locationSearchTable.handleMapSearchDelegate = self
}
}
LOCATIONSEARCHTABLE code:
import UIKit
import MapKit
class LocationSearchTable: UITableViewController {
var matchingItems:[MKMapItem] = []
var mapView: MKMapView? = nil
var handleMapSearchDelegate:HandleMapSearch? = nil
override func viewDidLoad() {
super.viewDidLoad()
}
func parseAddress(selectedItem:MKPlacemark) -> String {
// put a space between "4" and "Melrose Place"
let firstSpace = (selectedItem.subThoroughfare != nil && selectedItem.thoroughfare != nil) ? " " : ""
// put a comma between street and city/state
let comma = (selectedItem.subThoroughfare != nil || selectedItem.thoroughfare != nil) && (selectedItem.subAdministrativeArea != nil || selectedItem.administrativeArea != nil) ? ", " : ""
// put a space between "Washington" and "DC"
let secondSpace = (selectedItem.subAdministrativeArea != nil && selectedItem.administrativeArea != nil) ? " " : ""
let addressLine = String(
format:"%#%#%#%#%#%#%#",
// street number
selectedItem.subThoroughfare ?? "",
firstSpace,
// street name
selectedItem.thoroughfare ?? "",
comma,
// city
selectedItem.locality ?? "",
secondSpace,
// state
selectedItem.administrativeArea ?? ""
)
return addressLine
}
}
extension LocationSearchTable : UISearchResultsUpdating {
func updateSearchResults(for searchController: UISearchController){
guard let mapView = mapView,
let searchBarText = searchController.searchBar.text else { return }
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchBarText
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.start { response, _ in
guard let response = response else {
return
}
self.matchingItems = response.mapItems
self.tableView.reloadData()
}
}
}
extension LocationSearchTable {
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return matchingItems.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell")!
let selectedItem = matchingItems[indexPath.row].placemark
cell.textLabel?.text = selectedItem.name
cell.detailTextLabel?.text = parseAddress(selectedItem: selectedItem)
return cell
}
}

UIGestureRecognizer in Swift Playgrounds

I am trying to integrate UIGestureRecognizer into my swift playground so that wherever I tap in my playground, my sprite goes to that point. I've tried everything! Watching countless youtube videos, and going on stack overflow for answers but everyone starts with making a class called view that is a UIView and in their code they keep referring to "self". I instead made a variable named "view" that was a SKView. I try to get parts of their code, put it in mine, and change it, but it just doesn't work. Here's the work that I got so far.
view.addGestureRecognizer(UIGestureRecognizer(target: view, action: #selector(handleTap(sender:))))
func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
My playground keeps telling me that i'm using an unresolved identifier 'handleTap(sender:)'
UIGestureRecognizer example with different states of Gesture recogniser in Playground
swift3+
import UIKit
import PlaygroundSupport
class ViewController : UIViewController {
var yellowView: UIView!
var redView: UIView!
var yellowViewOrigin: CGPoint!
override func loadView() {
// UI
let view = UIView()
view.backgroundColor = .white
yellowView = UIView()
yellowView.backgroundColor = .yellow
view.addSubview(yellowView)
redView = UIView()
redView.backgroundColor = .red
view.addSubview(redView)
// Layout
redView.translatesAutoresizingMaskIntoConstraints = false
yellowView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
yellowView.topAnchor.constraint(equalTo: view.topAnchor, constant: 20.0),
yellowView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
yellowView.heightAnchor.constraint(equalToConstant: 80.0),
yellowView.widthAnchor.constraint(equalToConstant: 80.0),
redView.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -20.0),
redView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
redView.heightAnchor.constraint(equalToConstant: 80.0),
redView.widthAnchor.constraint(equalToConstant: 80.0)
])
self.view = view
}
override func viewDidLoad() {
super.viewDidLoad()
let pan = UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture(_:)))
yellowView.addGestureRecognizer(pan)
yellowViewOrigin = yellowView.frame.origin
view.bringSubview(toFront: yellowView)
}
#objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
let targetView = sender.view!
let translation = sender.translation(in: view)
switch sender.state {
case .began,.changed:
targetView.center = CGPoint(x: targetView.center.x + translation.x
,y: targetView.center.y + translation.y)
sender.setTranslation(CGPoint.zero, in: view)
case .ended:
if targetView.frame.intersects(redView.frame){
UIView.animate(withDuration: 0.3) {
targetView.alpha = 0.0
}
}
else{
UIView.animate(withDuration: 0.3) {
targetView.frame.origin = self.yellowViewOrigin
}
}
break
default:
break
}
}
}
PlaygroundPage.current.liveView = ViewController()
I think you need to add the #objc mark in front of the function declaration, so that handleTap will be visible to #selector.
#objc func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
func handleTap has to be called from your view type.
Please try to add it inside an SKView extension:
extension SKView {
func handleTap(sender: UITapGestureRecognizer) {
player.position = sender.location(in: view)
}
}