xibs in horizontal scrollview in swift - swift

my requirement is i have horizontal scroll view in second half of the screen in that second half i am loading xib as a swipe layout and my first half i have bar code reader.click here for image
if user user click on scan button camera will and it will scan the bar code and get output if user in the first view in scroll then scaned value should be in first xibs textview and the code is
import UIKit
import Alamofire
import AVFoundation
class sgstockallocationserialnumberViewController: SGBaseViewController,UIScrollViewDelegate,AVCaptureMetadataOutputObjectsDelegate {
#IBOutlet weak var serialnumberprocessezsorcancelview: UIView!
#IBOutlet weak var txtseialnumbers: UITextView!
#IBOutlet weak var clientview: UIView!
#IBOutlet weak var scroll_horizontal: UIScrollView!
#IBOutlet weak var serialinternalview: UIView!
#IBOutlet weak var cameraview: UIView!
var featureArray = [Dictionary<String,String>]()
var stockList: [Parameters] = []
var captureSession: AVCaptureSession!
var previewLayer: AVCaptureVideoPreviewLayer!
var output = AVCaptureMetadataOutput()
var orderlines: sgpurchaseorderorderlinewithmsg?
var arrayofcode:[String] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
serialnumberprocessezsorcancelview.layer.cornerRadius = 5
serialnumberprocessezsorcancelview.layer.shadowColor = UIColor.gray.cgColor
serialnumberprocessezsorcancelview.layer.shadowOffset = CGSize(width: 2, height: 2)
serialnumberprocessezsorcancelview.layer.shadowRadius = 5
serialnumberprocessezsorcancelview.layer.shadowOpacity = 1.0
clientview.layer.cornerRadius = 5
print(scroll_horizontal.contentSize)
scroll_horizontal.showsHorizontalScrollIndicator = false
scroll_horizontal.delegate = self
let tap: UITapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(sgstockallocationserialnumberViewController.dismissKeyboard))
view.addGestureRecognizer(tap)
if SGManager.sharedManager.getorderlines()?.data != nil{
let modeldata = SGManager.sharedManager.getorderlines()
self.orderlines = modeldata
for i in 0...(orderlines?.data.count)! - 1 {
let cell_details =
["title":self.orderlines?.data[i].productName,"Quantity":String(describing:self.orderlines!.data[i].quantity!)] as [String : Any]
featureArray.append(cell_details as! [String : String])
}
let cell_details = ["title":"Miscellaneous","Quantity":""]
featureArray.append(cell_details )
}
}
override func viewDidAppear(_ animated: Bool) {
scroll_horizontal.isPagingEnabled = true
scroll_horizontal.contentSize = CGSize(width: self.serialinternalview.frame.size.width * CGFloat(featureArray.count), height: 188)
self.loadFeatures()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
}
func dismissKeyboard() {
view.endEditing(true)
}
func loadFeatures() {
for (index, feature) in featureArray.enumerated() {
if let featureView = Bundle.main.loadNibNamed("Feature", owner: self, options: nil)?.first as? FeatureView {
featureView.seraialnumbertextareaview.layer.borderColor = UIColor.UIColorFromRGB(fromHex: 0xD6D6D6).cgColor
featureView.seraialnumbertextareaview.layer.borderWidth = 1
featureView.internalview_xib.layer.borderColor = UIColor.UIColorFromRGB(fromHex: 0xD6D6D6).cgColor
featureView.internalview_xib.layer.borderWidth = 1
featureView.internalview_xib.layer.cornerRadius = 5
featureView.internalview_xib.layer.masksToBounds = true
featureView.seraialnumbertextareaview.layer.cornerRadius = 5
featureView.seraialnumbertextareaview.layer.masksToBounds = true
featureView.lblnetworkname.text = feature["title"]
featureView.lblquantity_xib.text = feature["Quantity"]
var textview_ = ""
featureView.tag = index
scroll_horizontal.addSubview(featureView)
featureView.frame.size.width = self.serialinternalview.frame.width
featureView.frame.origin.x = CGFloat(index) * self.serialinternalview.frame.size.width
print(featureView.frame.origin.x)
print(self.serialinternalview.bounds.size.width)
}
}
}
func buyFeature (sender:UIButton) {
print("The user wants to buy feature \(sender.tag)")
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
let page = scrollView.contentOffset.x / scrollView.frame.size.width
// print(Int(page))
// featurePageControl.currentPage = Int(page)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func process(_ sender: UIButton) {
if txtseialnumbers.text != ""{
getnewpurchaseorderfiltermanufacturer()
}
else {
let alert = UIAlertController(title: "No serial numbers detected", message:"add the serial numbers", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
#IBAction func cancel(_ sender: UIButton) {
}
func getnewpurchaseorderfiltermanufacturer(){
stockList.removeAll()
let stocklistintrernal : Parameters = [
"productid" :0,
"productCategoryId" : 1, "productParameterValue": txtseialnumbers.text]
print(txtseialnumbers.text)
stockList.append(stocklistintrernal)
UserDefaults.standard.setValue(txtseialnumbers.text, forKey: "serialnumbers")
let clientid_stockallocation = UserDefaults.standard.object(forKey: "clientidfromstockallocation")
let params: [String : Any] = ["productParameterValue" :stockList,"isCommit":"False","toClientId":clientid_stockallocation]
SGWebServices.sharedWebServices.stockalloction(params:params) { (error, response) in
if (error == nil) {
var purchaseordermanufacturer:sgstockallocationwhenserialnumberpassed?
purchaseordermanufacturer = response
// print(purchaseordermanufacturer?.message)
if purchaseordermanufacturer?.message == "Success"
{
let userDefaults = UserDefaults.standard
let encodedData = NSKeyedArchiver.archivedData(withRootObject: response!)
userDefaults.set(encodedData, forKey: "summary")
userDefaults.synchronize()
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SGStockallocationsummaryViewController")
UserDefaults.standard.set(self.stockList, forKey: "stoclist")
self.present(controller, animated: true, completion: nil)
}
else
{
let alert = UIAlertController(title: "", message: purchaseordermanufacturer?.message, preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
// print("success")
}
else{
let alert = UIAlertController(title: "!", message: String(describing: error), preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}}}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
if (captureSession?.isRunning == false) {
captureSession.startRunning();
}
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
if (captureSession?.isRunning == true) {
captureSession.stopRunning();
}
}
#IBAction func scanstarted(_ sender: UIButton) {
barcodereaderon()
}
#IBAction func scanstop(_ sender: UIButton) {
stopsession()
print(arrayofcode.count)
}
func barcodereaderon(){
// view.backgroundColor = UIColor.black
captureSession = AVCaptureSession()
let videoCaptureDevice = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
let videoInput: AVCaptureDeviceInput
do {
videoInput = try AVCaptureDeviceInput(device: videoCaptureDevice)
} catch {
return
}
if (captureSession.canAddInput(videoInput)) {
captureSession.addInput(videoInput)
} else {
failed();
return;
}
let metadataOutput = AVCaptureMetadataOutput()
if (captureSession.canAddOutput(metadataOutput)) {
captureSession.addOutput(metadataOutput)
metadataOutput.setMetadataObjectsDelegate(self, queue: DispatchQueue.main)
metadataOutput.metadataObjectTypes = [AVMetadataObjectTypeEAN8Code,
AVMetadataObjectTypeEAN13Code,
AVMetadataObjectTypePDF417Code,
AVMetadataObjectTypeQRCode,
AVMetadataObjectTypeUPCECode,
AVMetadataObjectTypeCode39Code,
AVMetadataObjectTypeCode39Mod43Code,
AVMetadataObjectTypeCode93Code,
AVMetadataObjectTypeCode128Code,
AVMetadataObjectTypeAztecCode]
} else {
failed()
return
}
previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
previewLayer.frame = cameraview.layer.bounds;
previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
cameraview.layer.addSublayer(previewLayer)
captureSession.startRunning();
}
func stopsession(){
captureSession.stopRunning()
cameraview.backgroundColor = UIColor.black
previewLayer.removeFromSuperlayer()
}
func failed() {
let ac = UIAlertController(title: "Scanning not supported", message: "Your device does not support scanning a code from an item. Please use a device with a camera.", preferredStyle: .alert)
ac.addAction(UIAlertAction(title: "OK", style: .default))
present(ac, animated: true)
captureSession = nil
}
func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [Any]!, from connection: AVCaptureConnection!) {
// captureSession.stopRunning()
if let metadataObject = metadataObjects.first {
let readableObject = metadataObject as! AVMetadataMachineReadableCodeObject;
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
// dismiss(animated: true)
found(code: readableObject.stringValue);
}
}
func found(code: String) {
if arrayofcode.contains(code) {
}
else{
let page = scroll_horizontal.contentOffset.x / scroll_horizontal.frame.size.width
print(page)
var featureView = Bundle.main.loadNibNamed("Feature", owner: self, options: nil)?.first as? FeatureView
// featureView?.tag = Int(page)
// var tmpButton = featureView?.viewWithTag(Int(page)) as? tea
print(Int(page))
featureView = featureView?.viewWithTag(Int(page)) as! FeatureView?
featureView?.textarea_xib.text = code
print(featureView?.textarea_xib.text!)
arrayofcode.append(code)
var barcodeinarraay:[String] = []
arrayofcode.append(code)
}
// UserDefaults.standard.set(code, forKey: "code")
// UserDefaults.standard.synchronize()
print(code)
// arrayofcode.append(code)
// let storyboard = UIStoryboard(name: "Main", bundle: nil)
// let controller = storyboard.instantiateViewController(withIdentifier: "screenViewController")
// self.present(controller, animated: true, completion: nil)
//
}
override var prefersStatusBarHidden: Bool {
return true
}
override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
return .portrait
}
}

Related

Getting error when using Google place api in swift

I am using google place api to get location in my swift application. In my app when i am going to place api screen by clicking on the textfield present inside the cell of my tableview it is working fine. But when i am using the same code to show the place api screen by clicking on the textfield present inside the uiview, after selecting any place it is comming back to the previous screen, but once again the place api screen opening automatically. i have added my code, can anyonce see it and let me know the solution?
import GooglePlaces
class AddPatientViewController: UIViewController{
//MARK:- Properties
#IBOutlet weak var location_TextField: UITextField!
//MARK:- Variables
var locationManager = CLLocationManager()
var mAppDelegate:AppDelegate = UIApplication.shared.delegate as! AppDelegate;
//MARK:- LifeCycles Methods
override func viewDidLoad() {
super.viewDidLoad()
setDelegate()
}
//MARK:- Helpers
private func setDelegate(){
location_TextField.delegate = self
}
//MARK: - getLocation
func getCurrentLatLongFromCLlocation() {
locationManager.desiredAccuracy = kCLLocationAccuracyBest
self.locationManager.requestWhenInUseAuthorization()
if CLLocationManager.locationServicesEnabled() {
switch CLLocationManager.authorizationStatus() {
case .notDetermined, .restricted, .denied:
print("No access")
getPermissionAgain()
case .authorizedAlways, .authorizedWhenInUse:
print("Access")
if let coordinateValue = locationManager.location?.coordinate {
mAppDelegate.cordinateVal = coordinateValue
self.getPlaceDataUsingLocation()
}
#unknown default:
getPermissionAgain()
}
} else {
print("Location services are not enabled")
getPermissionAgain()
}
}
func getPermissionAgain(){
let alert = UIAlertController(title: "Simplr", message: "Required location permission to set your search location", preferredStyle: .alert)
let okayAction = UIAlertAction(title: "Dismiss", style: .default, handler: nil)
let settingsAction = UIAlertAction(title: "Settings", style: .default) { (action) in
if let appSettings = URL(string: UIApplication.openSettingsURLString) {
if #available(iOS 10.0, *) {
UIApplication.shared.open(appSettings, options: [:], completionHandler: { (enabled) in
DispatchQueue.main.async {
//UIApplication.shared.registerForRemoteNotifications()
}
})
} else {
// Fallback on earlier versions
}
}
}
alert.addAction(okayAction)
alert.addAction(settingsAction)
present(alert, animated: false, completion: nil)
}
func getPlaceDataUsingLocation(){
DispatchQueue.global().async {
// Specify the place data types to return.
let placesClient = GMSPlacesClient.init()
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) | UInt(GMSPlaceField.coordinate.rawValue) | UInt(GMSPlaceField.formattedAddress.rawValue))
placesClient.currentPlace { (list, error) in
if let error = error {
print("An error occurred: \(error.localizedDescription)")
return
}
if let placeLikelihoodList = list?.likelihoods {
for likelihood in placeLikelihoodList {
let place = likelihood.place
print("\n\n")
print("Current Place name \(String(describing: place.name)) at likelihood \(likelihood.likelihood)")
print("Current PlaceID \(String(describing: place.placeID))")
print("Formatted Address: \(String(describing: place.formattedAddress))")
// print("Fields: \(place.)")
print("\nAddress Component: \(String(describing: place.addressComponents))\n")
print("Type: \(String(describing: place.types))")
}
}
}
}
}
}
//MARK: - UITextfield Delegate Methods
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool{
if textField == location_TextField{
let autocompleteController = GMSAutocompleteViewController()
autocompleteController.delegate = self
// autocompleteController.primaryTextColor = UIColor(hex: "#4C2B6D")
// autocompleteController.secondaryTextColor = UIColor(hex: "#4C2B6D")
// Specify the place data types to return.
let fields: GMSPlaceField = GMSPlaceField(rawValue: UInt(GMSPlaceField.name.rawValue) |
UInt(GMSPlaceField.placeID.rawValue) | UInt(GMSPlaceField.coordinate.rawValue) |
UInt(GMSPlaceField.formattedAddress.rawValue) | UInt(GMSPlaceField.addressComponents.rawValue))
autocompleteController.placeFields = fields
autocompleteController.navigationController?.navigationBar.barTintColor = .blue
autocompleteController.navigationController?.navigationBar.prefersLargeTitles = true
// Specify a filter.
let filter = GMSAutocompleteFilter()
filter.type = .geocode
autocompleteController.autocompleteFilter = filter
// Display the autocomplete view controller.
autocompleteController.modalPresentationStyle = .fullScreen
present(autocompleteController, animated: true, completion: nil)
}
return true
}
}
//MARK: - GMSAutocompleteViewControllerDelegate
#available(iOS 13.0, *)
extension AddPatientViewController: GMSAutocompleteViewControllerDelegate {
// Handle the user's selection.
func viewController(_ viewController: GMSAutocompleteViewController, didAutocompleteWith place: GMSPlace) {
print("selected place is \(getFormattedAddressFromAddressComponents(place: place))")
location_TextField.text = getFormattedAddressFromAddressComponents(place: place)
self.dismiss(animated: true)
}
func viewController(_ viewController: GMSAutocompleteViewController, didFailAutocompleteWithError error: Error) {
// TODO: handle the error.
print("Error: ", error.localizedDescription)
}
// User canceled the operation.
func wasCancelled(_ viewController: GMSAutocompleteViewController) {
self.dismiss(animated: true)
}
// Turn the network activity indicator on and off again.
func didRequestAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didUpdateAutocompletePredictions(_ viewController: GMSAutocompleteViewController) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
}
func getFormattedAddressFromAddressComponents(place: GMSPlace) -> String{
var neighborhood = ""
var locality = ""
var sublocality = ""
var administrative_area_level_1 = ""
var formatedAddress = ""
if let addressComponents : Array<GMSAddressComponent> = place.addressComponents{
print(addressComponents)
for component in addressComponents{
if component.types.contains("administrative_area_level_1"){
if let shortName = component.shortName{
administrative_area_level_1 = shortName
}
}
else if component.types.contains("neighborhood")
{
neighborhood = component.name
} else if component.types.contains("sublocality")
{
sublocality = component.name
}else if component.types.contains("locality")
{
locality = component.name
}
}
var shouldAddComma = false
if !isNullObject(anyObject: administrative_area_level_1){
shouldAddComma = true
}
if !isNullObject(anyObject: neighborhood){
formatedAddress = neighborhood
}else if !isNullObject(anyObject: sublocality){
formatedAddress = sublocality
}
else if !isNullObject(anyObject: locality){
formatedAddress = locality
}
if !isNullObject(anyObject: formatedAddress){
if shouldAddComma{
formatedAddress = "\(formatedAddress),\(administrative_area_level_1)"
}
}
}
return formatedAddress
}
}

Swift - Visionkit How to edit the color of the buttons "Keep Scan" and "Retake"

I have a problem.
I've implemented the visionkit framework in order to detect a text when the camera is used.
The problem is that the colors of the buttons: "Keep Scan" and "Retake" are blue. I want them in black color. How can I fix this problem? I paste my code.
Thank you in advance!
import UIKit
import Vision
import VisionKit
class ScanText: UIViewController, VNDocumentCameraViewControllerDelegate {
#IBOutlet weak var imageView: UIImageView!
#IBOutlet weak var TextScan: UITextView!
#IBOutlet weak var buttonStartShape: UIButton!
#IBOutlet weak var infoScattaUnaFoto: UILabel!
#IBOutlet weak var copyButtonShape: UIButton!
var textRecognitionRequest = VNRecognizeTextRequest(completionHandler: nil)
private let textRecognitionWorkQueue = DispatchQueue(label: "MyVisionScannerQueue", qos: .userInitiated, attributes: [], autoreleaseFrequency: .workItem)
var classView = ViewController()
var arrayText = String()
override func viewDidLoad() {
super.viewDidLoad()
imageView.layer.cornerRadius = 15
imageView.clipsToBounds = true
TextScan.layer.cornerRadius = 15
TextScan.clipsToBounds = true
copyButtonShape.layer.cornerRadius = 25
copyButtonShape.clipsToBounds = true
buttonStartShape.layer.cornerRadius = 25
buttonStartShape.clipsToBounds = true
TextScan.isEditable = true
setupVision()
}
#IBAction func TakePicture(_ sender: Any) {
let scannerViewController = VNDocumentCameraViewController()
scannerViewController.delegate = self
present(scannerViewController, animated: true)
}
private func setupVision() {
textRecognitionRequest = VNRecognizeTextRequest { (request, error) in
guard let observations = request.results as? [VNRecognizedTextObservation] else { return }
var detectedText = ""
for observation in observations {
guard let topCandidate = observation.topCandidates(2).first else { return }
print("text \(topCandidate.string) has confidence \(topCandidate.confidence)")
detectedText += topCandidate.string
detectedText += "\n"
}
DispatchQueue.main.async {
self.TextScan.isHidden = false
self.infoScattaUnaFoto.isHidden = true
self.copyButtonShape.isHidden = false
self.buttonStartShape.setTitle("Ripeti", for: .normal)
self.TextScan.text += detectedText
self.TextScan.flashScrollIndicators()
}
}
textRecognitionRequest.recognitionLevel = .accurate
}
private func processImage(_ image: UIImage) {
imageView.image = image
recognizeTextInImage(image)
}
private func recognizeTextInImage(_ image: UIImage) {
guard let cgImage = image.cgImage else { return }
TextScan.text = ""
textRecognitionWorkQueue.async {
let requestHandler = VNImageRequestHandler(cgImage: cgImage, options: [:])
do {
try requestHandler.perform([self.textRecognitionRequest])
} catch {
print(error)
}
}
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFinishWith scan: VNDocumentCameraScan) {
/* guard scan.pageCount >= 1 else {
controller.dismiss(animated: true)
return
}*/
for i in 0 ..< scan.pageCount {
let img = scan.imageOfPage(at: i)
processImage(img)
}
let originalImage = scan.imageOfPage(at: 0)
print(originalImage)
//let newImage = compressedImage(originalImage)
controller.dismiss(animated: true)
}
func documentCameraViewController(_ controller: VNDocumentCameraViewController, didFailWithError error: Error) {
print(error)
controller.dismiss(animated: true)
}
func documentCameraViewControllerDidCancel(_ controller: VNDocumentCameraViewController) {
controller.dismiss(animated: true)
}
func compressedImage(_ originalImage: UIImage) -> UIImage {
guard let imageData = originalImage.jpegData(compressionQuality: 1),
let reloadedImage = UIImage(data: imageData) else {
return originalImage
}
return reloadedImage
}
//MARK: By tapping on this button, I pass all the data.
#IBAction func CopyText(_ sender: Any) {
let vc = (storyboard?.instantiateViewController(identifier: "SpeakDetail") as? ViewController)!
vc.textscannerized = TextScan.text
self.navigationController?.pushViewController(vc, animated: true)
}
}
You can try something like this:
scannerViewController.view.tintColor = .red

Why does my segue not wait until completion handler finished?

I have a page based app, using RootViewController, ModelViewController, DataViewController, and a SearchViewController.
In my searchViewController, I search for an item and then add or remove that Item to an array which is contained in a Manager class(and UserDefaults), which the modelViewController uses to instantiate an instance of DataViewController with the correct information loaded using the dataObject. Depending on whether an Item was added or removed, I use a Bool to determine which segue was used, addCoin or removeCoin, so that the RootViewController(PageView) will show either the last page in the array, (when a page is added) or the first (when removed).
Everything was working fine until I ran into an error which I can not diagnose, the problem is that when I add a page, the app crashes, giving me a "unexpectadely found nil when unwrapping an optional value"
This appears to be the problem function, in the searchViewController 'self.performSegue(withIdentifier: "addCoin"' seems to be called instantly, even without the dispatchque:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
Meaning that In my DataViewController, this function will find nil:
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
I can't find out why it does not wait for completion.
I also get this error about threads:
[Assert] Cannot be called with asCopy = NO on non-main thread.
which is why I try to do the push segue using dispatch que
Here is my searchViewController full code:
import UIKit
class SearchViewController: UIViewController, UISearchBarDelegate {
let selectionLabel = UILabel()
let searchBar = UISearchBar()
let addButton = UIButton()
let removeButton = UIButton()
var filteredObject: [String] = []
var dataObject = ""
var isSearching = false
//Add Button Action.
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
CGPrices.shared.getData(arr: true, completion: { (success) in
print(Manager.shared.coins)
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
})
}
searchBar.text = ""
}
//Remove button action.
#objc func removeButtonActon(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.removeCoin(coin: dataObject)
self.performSegue(withIdentifier: "addCoin", sender: self)
}
searchBar.text = ""
}
//Prepare for segue, pass removeCoinSegue Bool depending on remove or addCoin.
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = true
}
} else if segue.identifier == "addCoin" {
if let destinationVC = segue.destination as? RootViewController {
destinationVC.addCoinSegue = false
}
}
}
//Remove button action.
#objc func removeButtonAction(sender: UIButton!) {
if Manager.shared.coins.count == 1 {
removeAlert()
} else {
Manager.shared.removeCoin(coin: dataObject)
print(Manager.shared.coins)
print(dataObject)
searchBar.text = ""
self.removeButton.isHidden = true
DispatchQueue.main.async {
self.performSegue(withIdentifier: "removeCoin", sender: self)
}
}
}
//Search/Filter the struct from CGNames, display both the Symbol and the Name but use the ID as dataObject.
func filterStructForSearchText(searchText: String, scope: String = "All") {
if !searchText.isEmpty {
isSearching = true
filteredObject = CGNames.shared.coinNameData.filter {
// if you need to search key and value and include partial matches
// $0.key.contains(searchText) || $0.value.contains(searchText)
// if you need to search caseInsensitively key and value and include partial matches
$0.name.range(of: searchText, options: .caseInsensitive) != nil || $0.symbol.range(of: searchText, options: .caseInsensitive) != nil
}
.map{ $0.id }
} else {
isSearching = false
print("NoText")
}
}
//Running filter function when text changes.
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
filterStructForSearchText(searchText: searchText)
if isSearching == true && filteredObject.count > 0 {
addButton.isHidden = false
dataObject = filteredObject[0]
selectionLabel.text = dataObject
if Manager.shared.coins.contains(dataObject) {
removeButton.isHidden = false
addButton.isHidden = true
} else {
removeButton.isHidden = true
addButton.isHidden = false
}
} else {
addButton.isHidden = true
removeButton.isHidden = true
selectionLabel.text = "e.g. btc/bitcoin"
}
}
override func viewDidLoad() {
super.viewDidLoad()
//Setup the UI.
self.view.backgroundColor = .gray
setupView()
}
override func viewDidLayoutSubviews() {
}
//Hide keyboard
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
self.view.endEditing(true)
}
//Alerts
func removeAlert() {
let alertController = UIAlertController(title: "Can't Remove", message: "\(dataObject) can't be deleted, add another to delete \(dataObject)", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func Duplicate() {
let alertController = UIAlertController(title: "Duplicate", message: "\(dataObject) is already in your pages!", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
func max() {
let alertController = UIAlertController(title: "Maximum Reached", message: "\(dataObject) can't be added, you have reached the maximum of 5 coins. Please delete a coin to add another.", preferredStyle: .alert)
alertController.addAction(UIAlertAction(title: "Okay", style: .default, handler: nil))
self.present(alertController, animated: true, completion: nil)
}
}
and here is the DataViewController
import UIKit
class DataViewController: UIViewController {
#IBOutlet weak var dataLabel: UILabel!
//Variables and Objects.
//The dataObject carries the chosen cryptocurrencies ID from the CoinGecko API to use to get the correct data to load on each object.
var dataObject = String()
//The DefaultCurrency (gbp, eur...) chosen by the user.
var defaultCurrency = ""
//The Currency Unit taken from the exchange section of the API.
var currencyUnit = CGExchange.shared.exchangeData[0].rates.gbp.unit
var secondaryUnit = CGExchange.shared.exchangeData[0].rates.eur.unit
var tertiaryUnit = CGExchange.shared.exchangeData[0].rates.usd.unit
//Index of the dataObject
var dataIndex = Int()
//Objects
let cryptoLabel = UILabel()
let cryptoIconImage = UIImageView()
let secondaryPriceLabel = UILabel()
let mainPriceLabel = UILabel()
let tertiaryPriceLabel = UILabel()
//Custom Fonts.
let customFont = UIFont(name: "AvenirNext-Heavy", size: UIFont.labelFontSize)
let secondFont = UIFont(name: "AvenirNext-BoldItalic" , size: UIFont.labelFontSize)
//Setup Functions
//Get the index of the dataObject
func getIndex() {
let index = CGPrices.shared.coinData.index(where: { $0.id == dataObject })!
dataIndex = index
}
//Label
func setupLabels() {
//cryptoLabel from dataObject as name.
cryptoLabel.text = CGPrices.shared.coinData[dataIndex].name
//Prices from btc Exchange rate.
let btcPrice = CGPrices.shared.coinData[dataIndex].current_price!
let dcExchangeRate = CGExchange.shared.exchangeData[0].rates.gbp.value
let secondaryExchangeRate = CGExchange.shared.exchangeData[0].rates.eur.value
let tertiaryExchangeRate = CGExchange.shared.exchangeData[0].rates.usd.value
let realPrice = (btcPrice * dcExchangeRate)
let secondaryPrice = (btcPrice * secondaryExchangeRate)
let tertiaryPrice = (btcPrice * tertiaryExchangeRate)
secondaryPriceLabel.text = "\(secondaryUnit)\(String((round(1000 * secondaryPrice) / 1000)))"
mainPriceLabel.text = "\(currencyUnit)\(String((round(1000 * realPrice) /1000)))"
tertiaryPriceLabel.text = "\(tertiaryUnit)\(String((round(1000 * tertiaryPrice) / 1000)))"
}
//Image
func getIcon() {
let chosenImage = CGPrices.shared.coinData[dataIndex].image
let remoteImageUrl = URL(string: chosenImage)
guard let url = remoteImageUrl else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
DispatchQueue.main.async {
self.cryptoIconImage.image = UIImage(data: data)
}
}
}.resume()
}
override func viewDidLoad() {
super.viewDidLoad()
// for family in UIFont.familyNames.sorted() {
// let names = UIFont.fontNames(forFamilyName: family)
// print("Family: \(family) Font names: \(names)")
// }
// Do any additional setup after loading the view, typically from a nib.
self.setupLayout()
self.getIndex()
self.setupLabels()
self.getIcon()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
self.dataLabel!.text = dataObject
view.backgroundColor = .lightGray
}
}
Edit: CGPrices Class with getData method:
import Foundation
class CGPrices {
struct Coins: Decodable {
let id: String
let name: String
let symbol: String
let image: String
let current_price: Double?
let low_24h: Double?
//let price_change_24h: Double?
}
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i + 1) < coins.count { coinsEncoded += "%2C" }
}
print("encoded")
}
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
guard let url = URL(string: urlJSON) else { return }
URLSession.shared.dataTask(with: url) { (data, response, err) in
guard let data = data else { return }
do {
let coinsData = try JSONDecoder().decode([Coins].self, from: data)
self.coinData = coinsData
completion(arr)
} catch let jsonErr {
print("error serializing json: \(jsonErr)")
print(data)
}
}.resume()
}
func refresh(completion: () -> ()) {
defaultCurrency = UserDefaults.standard.string(forKey: "DefaultCurrency")!
completion()
}
}
I figured it out.
The problem was inside my getData method I was not updated the coins array:
var coinData = [Coins]()
var defaultCurrency = ""
var coins = Manager.shared.coins
var coinsEncoded = ""
static let shared = CGPrices()
func encode() {
for i in 0..<coins.count {
coinsEncoded += coins[i]
if (i+1)<coins.count { coinsEncoded+="%2C" }
}
print("encoded")
}
I needed to add this line in getData:
func getData(arr: Bool, completion: #escaping (Bool) -> ()) {
//Adding this line to update the array so that the URL is appended correctly.
coins = Manager.shared.coins
encode()
let urlJSON = "https://api.coingecko.com/api/v3/coins/markets?vs_currency=btc&ids=\(coinsEncoded)"
This would fix the finding nil in the DataViewController, but the app would still crash do to updating UI Elements on a background thread, as the segue was called inside the completion handler of the getData method. to fix this, I used DispatchQue.Main.Async on the segue inside the getData method in the addButton function, to ensure that everything is updated on the main thread, like so:
#objc func addButtonAction(sender: UIButton!) {
print("Button tapped")
if Manager.shared.coins.contains(dataObject) {
Duplicate()
} else if Manager.shared.coins.count == 5 {
max()
} else {
Manager.shared.addCoin(coin: dataObject)
print("starting")
CGPrices.shared.getData(arr: true) { (arr) in
print("complete")
print(CGPrices.shared.coinData)
//Here making sure it is updated on main thread.
DispatchQueue.main.async {
self.performSegue(withIdentifier: "addCoin", sender: self)
}
}
}
searchBar.text = ""
}
Thanks for all the comments as they helped me to figure this out, and I learned a lot in doing so. Hopefully this can help someone else in their thought process when debugging, as one can get so caught up in one area of a problem, and forget to take a step back and look to other areas.

How to clear the information from the Label.text?

I am developing a QR code reader application. Hier is my code:
var captureSession: AVCaptureSession?
var videoPreviewLayer: AVCaptureVideoPreviewLayer?
var qrCodeframeView: UIView?
#IBOutlet weak var CancelButton: UIButton!
#IBOutlet weak var Label: UILabel!
override func viewDidLoad() {
CancelButton.hidden = true
Label.hidden = true
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func ScanMe(sender: AnyObject) {
CancelButton.hidden = false
Label.hidden = false
let captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
var error: NSError?
let input: AnyObject!
do {
input = try AVCaptureDeviceInput (device: captureDevice)
} catch let error1 as NSError{
error = error1
input = nil
}
if (error != nil){
print ("\(error?.localizedDescription)")
return
}
captureSession = AVCaptureSession()
captureSession?.addInput(input as! AVCaptureInput)
let captureMetadatOutput = AVCaptureMetadataOutput()
captureSession?.addOutput(captureMetadatOutput)
captureMetadatOutput.setMetadataObjectsDelegate(self, queue: dispatch_get_main_queue())
captureMetadatOutput.metadataObjectTypes = [AVMetadataObjectTypeQRCode]
videoPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
videoPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
videoPreviewLayer?.frame = view.layer.bounds
view.layer.addSublayer(videoPreviewLayer!)
captureSession?.startRunning()
view.bringSubviewToFront(Label)
view.bringSubviewToFront(CancelButton)
qrCodeframeView = UIView()
qrCodeframeView?.layer.borderColor = UIColor.greenColor().CGColor
qrCodeframeView?.layer.borderWidth = 2
view.addSubview(qrCodeframeView!)
view.bringSubviewToFront(qrCodeframeView!)
}
func captureOutput(captureOutput: AVCaptureOutput!, didOutputMetadataObjects metadataObjects: [AnyObject]!, fromConnection connection: AVCaptureConnection!) {
if metadataObjects == nil || metadataObjects.count == 0 {
qrCodeframeView?.frame = CGRectZero
Label.text = "No QR code detected"
return
}
let metadateObj = metadataObjects[0] as! AVMetadataMachineReadableCodeObject
if metadateObj.type == AVMetadataObjectTypeQRCode {
let BarcodeObject = videoPreviewLayer?.transformedMetadataObjectForMetadataObject(metadateObj as AVMetadataMachineReadableCodeObject) as! AVMetadataMachineReadableCodeObject
qrCodeframeView?.frame = BarcodeObject.bounds
if metadateObj.stringValue != nil {
Label.text = metadateObj.stringValue
captureSession?.stopRunning()
}
}
}
#IBAction func Cancel(sender: AnyObject) {
CancelButton.hidden = true
Label.hidden = true
captureSession?.stopRunning()
qrCodeframeView?.removeFromSuperview()
videoPreviewLayer?.removeFromSuperlayer()
}
#IBAction func Open(sender: AnyObject) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var PC : SecondSecondViewController = segue.destinationViewController as! SecondSecondViewController
PC.label1 = Label.text!
}
}
The problem is when I click the cancel button and go back to the previous viewcontroller, when I reopen the qr code scanner I see the last scanned code, displayed in the Label.text. Would you please help me how to clear the label and do not display the old code, because these codes must be used only once and if the users see the codes they will be able to use them again.
If you want without adding other callbacks:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
var PC : SecondSecondViewController = segue.destinationViewController as! SecondSecondViewController
PC.label1 = Label.text!
Label.text = ""
}
otherwise you have to catch the unwind segue when you come back from SecondSecondViewController and there set Label.text = ""
Just add this to where you exit from the QR (press cancel):
label.text = ""

How to use presentViewController in a custom class?

I just update my code, but the alertWindow won't show up.
I add break point in Xcode, it shows that the viewDelegate is nil.
All delegates are nil.
This is my code:
class LoginViewController:UIViewController,AlertDelegate
{
override func viewDidLoad() {
super.viewDidLoad()
let BackGroundImage:UIImageView = UIImageView(frame: CGRectMake(0, 0, self.view.frame.width , self.view.frame.height))
let image: UIImage = UIImage(named: "backgroundLogin.jpg")!
BackGroundImage.image = image
self.view.addSubview(BackGroundImage)
//self.view.backgroundColor = UIColor(patternImage: UIImage(named: "backgroundLogin.jpg")!)
username.text = "User name"
password.text = "Password"
usernameWarning.text = ""
passwordWarning.text = ""
self.view.bringSubviewToFront(username)
self.view.bringSubviewToFront(password)
self.view.bringSubviewToFront(usernameText)
self.view.bringSubviewToFront(passwordText)
self.view.bringSubviewToFront(LoginButton)
self.view.bringSubviewToFront(RegisterButton)
self.view.bringSubviewToFront(usernameWarning)
self.view.bringSubviewToFront(passwordWarning)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
switch segue.identifier
{
case .Some("RegisterSegue"):
let registerViewController = segue.destinationViewController as! RegisterViewController
if let temp_register_username = usernameText!.text
{
registerViewController.register_name = temp_register_username
if let temp_register_pass = passwordText!.text
{
registerViewController.register_password = temp_register_pass
}
}
case .Some("loginSegue"):
let loginViewController = segue.destinationViewController as! FunctionsViewController
loginViewController.client = client
default:
super.prepareForSegue(segue, sender: sender)
}
}
override func shouldPerformSegueWithIdentifier(identifier: String, sender: AnyObject?) -> Bool {
if identifier == "loginSegue"
{
if usernameText.text != ""
{
if passwordText.text != ""
{
let encrypt = encryptMsg(send: passwordText.text!)
let encrypt_pass = encrypt.encryptSendMsg()
let sendMsg = AppConfig.AddHead("login", body: usernameText.text!+"|"+encrypt_pass)
let (_,_) = client.connect(timeout: 1)
let (success,_) = client.send(str: sendMsg)
if success
{
let data = client.read(1024*10)
if let d = data
{
if let str = String(bytes: d, encoding: NSUTF8StringEncoding)
{
if str[0...2] == AppConfig.TransmissionAgreementConfiguration["successLogin"]
{
AppConfig.StoreUserID(str)
let defaults = NSUserDefaults()
defaults.setObject(usernameText.text!, forKey: "userName")
let acceptThread = AcceptThread()
acceptThread.start()
return true
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginUsernameNotExist"]
{
usernameWarning.text = "User name doesn't exist"
usernameWarning.reloadInputViews()
passwordWarning.text = ""
passwordWarning.reloadInputViews()
return false
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginUsernameAlreadyOnline"]
{
usernameWarning.text = "User account already be online"
usernameWarning.reloadInputViews()
passwordWarning.text = ""
passwordWarning.reloadInputViews()
return false
}
else if str[0...2] == AppConfig.TransmissionAgreementConfiguration["errLoginPassword"]
{
usernameWarning.text = ""
usernameWarning.reloadInputViews()
passwordWarning.text = "Password Incorrect"
passwordWarning.reloadInputViews()
return false
}
}
}
}
return false
}
else
{
usernameWarning.text = ""
usernameWarning.reloadInputViews()
passwordWarning.text = "password cannot be empty"
passwordWarning.reloadInputViews()
return false
}
}
else
{
usernameWarning.text = "username cannot be empty"
usernameWarning.reloadInputViews()
return false
}
}
else
{
return true
}
}
func presentWindow(title:String,msg:String)
{
let alertController = UIAlertController(title: "\(title):", message: "\(msg)", preferredStyle: UIAlertControllerStyle.Alert)
alertController.addTextFieldWithConfigurationHandler{
(textField: UITextField!) -> Void in
textField.placeholder = "Message"
}
let cancelAction = UIAlertAction(title: "Ignore", style: UIAlertActionStyle.Cancel, handler: nil)
let okAction = UIAlertAction(title: "Reply", style: UIAlertActionStyle.Default,
handler: {
action in
let reply_msg = alertController.textFields!.first! as UITextField
print("I replies \(reply_msg)")
})
alertController.addAction(cancelAction)
alertController.addAction(okAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
#IBAction func logoutUnwindSegue(segue: UIStoryboardSegue) {
// Intentionally left blank
}
var client:TCPClient = TCPClient(addr:"localhost",port:8889)
var AppConfig=TransmissionAgreement(first:"test")
#IBOutlet weak var usernameWarning: UILabel!
#IBOutlet weak var passwordWarning: UILabel!
#IBOutlet weak var username: UILabel!
#IBOutlet weak var password: UILabel!
#IBOutlet weak var usernameText: UITextField!
#IBOutlet weak var passwordText: UITextField!
#IBOutlet weak var LoginButton: UIButton!
#IBOutlet weak var RegisterButton: UIButton!
}
class AcceptThread: NSThread {
override func main() {
let server:TCPServer = TCPServer(addr:"127.0.0.1",port: 7000)
let (success,_) = server.listen()
if success
{
while !cancelled
{
if let my_client = server.accept()
{
let clientReadThread = ClientReadThread(client: my_client)
clientReadThread.start()
clientThreads.append(clientReadThread)
}
}
for clientThread in clientThreads {
clientThread.cancel()
}
}
}
var clientThreads = Array<ClientReadThread>()
var AppConfig=TransmissionAgreement(first:"test")
}
#objc protocol AlertDelegate: class {
func presentWindow(title:String,msg:String)
}
#objc protocol ClientReadThreadDelegate: class {
func clientReadThread(clientReadThread: ClientReadThread, didReceiveMessage message: String)
}
class ClientReadThread:NSThread{
init(client: TCPClient) {
self.client = client
super.init()
}
override func main() {
while !cancelled, let readValue = client.read(1024*10) {
if let message = String(bytes: readValue, encoding: NSUTF8StringEncoding) {
let head = message[0...2]
// print("head is \(head)")
if head! == AppConfig.TransmissionAgreementConfiguration["add"]
{
let defaults = NSUserDefaults()
let myname = defaults.stringForKey("userName")
//store data
let context = CoreDataService.sharedCoreDataService.mainQueueContext
let friend = NSEntityDescription.insertNewObjectForNamedEntity(Friends.self, inManagedObjectContext: context)
friend.name = message.substringFromIndex(3)
try! context.save()
let sendMsg = AppConfig.AddClientHead("agreeAdd", body:myname)
let (_,_) = client.send(str:sendMsg)
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.delegate?.clientReadThread(self, didReceiveMessage: message)
})
}
else if head! == AppConfig.TransmissionAgreementConfiguration["chat"]
{
let search = message.substringFromIndex(3)
let resultController = try? catFriend.sharedCatFriendsService.catBlack(search)
try! resultController!.performFetch()
let flag = resultController?.sections?.count
if flag <= 1//friend
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let client_info = message.split("|")
let client_name = client_info[1]
//self.alertDelegate?.presentWindow(client_name, msg: client_info[2])
self.viewDelegate?.presentWindow(client_name, msg: client_info[2])
})
}
else//black, not show the meg
{
//do nothing
}
}
}
}
}
var AppConfig=TransmissionAgreement(first:"test")
let client: TCPClient
var viewDelegate:LoginViewController?
var delegate: ClientReadThreadDelegate?
var alertDelegate: AlertDelegate?
private var resultController: NSFetchedResultsController?
}
presentViewContoller is a method on UIViewController so you'll need an instance of a view controller to be able to present your alert view.
The way this would normally be done is to have the view controller you have start the ClientReadThread implement the AlertDelegate protocol. There's no reason for ClientReadThread to implement AlertDelegate itself. Would it ever be it's own delegate?
Also you delegate's should be marked weak or else you'll end up with a retain cycle.
class MyViewController : UIViewController, AlertDelegate {
private var clientReadThread: ClientReadThread?
override viewDidLoad() {
super.viewDidLoad();
clientReadThread = ClientReadThread(someTcpClient)
clientReadThread.viewDelegate = self
}
func presentWindow(title:String,msg:String) {
// Your code goes here.
}
}
Your other option would be to pass an instance of a UIViewController into your ClientReadThread and use that to present the alert view. Although that's not the way I would go. the ClientReadThread shouldn't need to know how it's alerts are handled, that should be delegated to another class.