In my app I need to use .gif and I did searched on it. Everyone asked to use UIImage.gifImageWithName("funny") or UIImage.gifWithName("jeremy") for adding .gif file. But I'm getting error on those Type 'UIImage' has no member 'gifWithName'. How to solve those Issues and How to use .gif in my app.
Download UIImage+Gif.swift file from https://github.com/bahlo/SwiftGif/tree/master/SwiftGifCommon and put into your project..
// An animated UIImage
let Gif = UIImage.gif(name: "jerry")
// A UIImageView with async loading
let imageView = UIImageView()
imageView.loadGif(name: "tom")]
iOS won't support .gif images directly. You have to use latest version of third party library like SDWebImage. The simplified solution is to use Webview.
Or else,
You can use UIImageView+Extension
this code seems to help you!
#IBOutlet weak var whiteView: UIImageView!
let imagePicker = UIImagePickerController()
override func viewDidLoad()
{
super.viewDidLoad()
GifView.loadGif(name: "funny")
imagePicker.delegate = self
}
Also, you have to add Controller for "loadGif" into your source and I think you can download on online.
//
// GifFunctions.swift
// GifFunctions
//
// Created by Ambu Sangoli on 11/12/16.
// Copyright © 2016 Ambu Sangoli. All rights reserved.
//
import UIKit
import ImageIO
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL = URL(string: gifUrl)
else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("image named \"\(gifUrl)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Save above code with any file name example GifFunctions.Swift add it to your project and use it like below
let gifToplay = UIImage.gifImageWithName("YourGifName")
YourImageView.image = (image: gifToplay)
Also you can use other methods.
Enjoy (y)
Related
I was working on a project which loads a gif to UIImageView from its url.
while running app in the Xcode gives a strange warning and the gif also looks laggy.
Warning is like:
Synchronous URL loading of
https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/5eeea355389655.59822ff824b72.gif
should not occur on this application's main thread as it may lead to
UI unresponsiveness. Please switch to an asynchronous networking API
such as URLSession.
The code snippet I've used for loading gif with url.
//
// iOSDevCenters+GIF.swift
// Get Fit
//
// Created by Sandeep Sahani on 08/12/22.
//
import UIKit
import ImageIO
// FIXME: comparison operators with optionals were removed from the Swift Standard Libary.
// Consider refactoring the code to use the non-optional operators.
fileprivate func < <T : Comparable>(lhs: T?, rhs: T?) -> Bool {
switch (lhs, rhs) {
case let (l?, r?):
return l < r
case (nil, _?):
return true
default:
return false
}
}
extension UIImage {
public class func gifImageWithData(_ data: Data) -> UIImage? {
guard let source = CGImageSourceCreateWithData(data as CFData, nil) else {
print("image doesn't exist")
return nil
}
return UIImage.animatedImageWithSource(source)
}
public class func gifImageWithURL(_ gifUrl:String) -> UIImage? {
guard let bundleURL:URL? = URL(string: gifUrl)
else {
print("image named \"\(gifUrl)\" doesn't exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL!) else {
print("image named \"\(gifUrl)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
public class func gifImageWithName(_ name: String) -> UIImage? {
guard let bundleURL = Bundle.main
.url(forResource: name, withExtension: "gif") else {
print("SwiftGif: This image named \"\(name)\" does not exist")
return nil
}
guard let imageData = try? Data(contentsOf: bundleURL) else {
print("SwiftGif: Cannot turn image named \"\(name)\" into NSData")
return nil
}
return gifImageWithData(imageData)
}
class func delayForImageAtIndex(_ index: Int, source: CGImageSource!) -> Double {
var delay = 0.1
let cfProperties = CGImageSourceCopyPropertiesAtIndex(source, index, nil)
let gifProperties: CFDictionary = unsafeBitCast(
CFDictionaryGetValue(cfProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDictionary).toOpaque()),
to: CFDictionary.self)
var delayObject: AnyObject = unsafeBitCast(
CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFUnclampedDelayTime).toOpaque()),
to: AnyObject.self)
if delayObject.doubleValue == 0 {
delayObject = unsafeBitCast(CFDictionaryGetValue(gifProperties,
Unmanaged.passUnretained(kCGImagePropertyGIFDelayTime).toOpaque()), to: AnyObject.self)
}
delay = delayObject as! Double
if delay < 0.1 {
delay = 0.1
}
return delay
}
class func gcdForPair(_ a: Int?, _ b: Int?) -> Int {
var a = a
var b = b
if b == nil || a == nil {
if b != nil {
return b!
} else if a != nil {
return a!
} else {
return 0
}
}
if a < b {
let c = a
a = b
b = c
}
var rest: Int
while true {
rest = a! % b!
if rest == 0 {
return b!
} else {
a = b
b = rest
}
}
}
class func gcdForArray(_ array: Array<Int>) -> Int {
if array.isEmpty {
return 1
}
var gcd = array[0]
for val in array {
gcd = UIImage.gcdForPair(val, gcd)
}
return gcd
}
class func animatedImageWithSource(_ source: CGImageSource) -> UIImage? {
let count = CGImageSourceGetCount(source)
var images = [CGImage]()
var delays = [Int]()
for i in 0..<count {
if let image = CGImageSourceCreateImageAtIndex(source, i, nil) {
images.append(image)
}
let delaySeconds = UIImage.delayForImageAtIndex(Int(i),
source: source)
delays.append(Int(delaySeconds * 1000.0)) // Seconds to ms
}
let duration: Int = {
var sum = 0
for val: Int in delays {
sum += val
}
return sum
}()
let gcd = gcdForArray(delays)
var frames = [UIImage]()
var frame: UIImage
var frameCount: Int
for i in 0..<count {
frame = UIImage(cgImage: images[Int(i)])
frameCount = Int(delays[Int(i)] / gcd)
for _ in 0..<frameCount {
frames.append(frame)
}
}
let animation = UIImage.animatedImage(with: frames,
duration: Double(duration) / 1000.0)
return animation
}
}
Place I have used the code.
//
// GifViewController.swift
// Get Fit
//
// Created by Sandeep Sahani on 08/12/22.
//
import UIKit
class GifViewController: UIViewController
{
#IBOutlet weak var gif: UIImageView!
override func viewDidLoad()
{
super.viewDidLoad()
let gifURL : String = "https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/5eeea355389655.59822ff824b72.gif"
let imageURL = UIImage.gifImageWithURL(gifURL)
self.gif.image = imageURL
}
}
How can I remove that strange warning and make that gif smooth?
The API call you're using is executed on the main thread so the system warns you about that.
I'd recommend using modern projects' default choice library for loading remote images called Kingfisher: https://github.com/onevcat/Kingfisher
There is a wiki section on loading gif images with Kingfisher lib:
https://github.com/onevcat/Kingfisher/wiki/Cheat-Sheet#animated-gif
Loading a GIF
let imageView: UIImageView = ...
imageView.kf.setImage(with: URL(string: "your_animated_gif_image_url")!)
or
let imageView = AnimatedImageView()
imageView.kf.setImage(with: URL(string: "your_large_animated_gif_image_url")!)
You have to add Kingfisher to your project as a Swift Package Manager project and then import Kingfisher at the top of the file
I've tried to build a delegate design pattern. I have a simple delegate in WeatherManager , but it's always nil.
I've tried to add weatherManager.delegate = self in override func viewDidLoad() of WeatherViewController.
However, I have another Protocol and delegate, which works fine.
I'm using the API to obtain geographic coordinates from city names, and the URL of the API to get information about the weather is created and executed.
WeatherManager
import Foundation
// delegate design parttern
protocol WeatherManagerDelegate:AnyObject {
func didUpdateWeather(inputWeatherModel: WeatherModel)
}
class WeatherManager {
// delegate
weak var delegate: WeatherManagerDelegate?
// fetchCoordinate
func fetchWeather(urlString: String) {
performRequest(inputURLString: urlString)
}
// performRequest
func performRequest(inputURLString: String) {
// 1. Create URL
if let url = URL(string: inputURLString) {
// 2. Create URLSession
let session = URLSession(configuration: .default)
// 3. Give the URLSession a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
if let weather = self.parseJSON(JSONobject: safeData) {
// ---------------- problem here -------------------
self.delegate?.didUpdateWeather(inputWeatherModel: weather)
}
}
}
// 4. Start the task
task.resume()
}
}
// parse JSON object
func parseJSON(JSONobject: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: JSONobject)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
print(error)
return nil
}
}
}
WeatherModel
import Foundation
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
var temperatureString: String {
return String(format: "%.1f", temperature)
}
var conditionName: String {
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud.rain"
case 600...622:
return "cloud.snow"
case 701...781:
return "cloud.fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}
WeatherViewController
import UIKit
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var cityLabel: UILabel!
let weatherManager = WeatherManager()
var coordinateManager = CoordinateManager()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
searchTextField.delegate = self
searchTextField.keyboardType = .asciiCapable
weatherManager.delegate = self
}
#IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Type Something"
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
coordinateManager.fetchCoordinate(cityName: city)
}
searchTextField.text = ""
}
func didUpdateWeather(inputWeatherModel: WeatherModel) {
print(inputWeatherModel.temperature)
}
}
CoordinateManager
import Foundation
struct CoordinateManager {
// WeatherManager
let weathetManager = WeatherManager()
// geographical coordinates(lat, lon) 地理座標
let coordinateURL = "https://api.openweathermap.org/geo/1.0/direct?limit=1&appid=c8e60c6317c653a1789294c00f54ae19"
// fetchCoordinate
func fetchCoordinate(cityName: String) {
let urlString = "\(coordinateURL)&q=\(cityName)"
performRequest(inputURLString: urlString)
}
// transformURLString
func transformURLString(_ string: String) -> URLComponents? {
guard let urlPath = string.components(separatedBy: "?").first else {
return nil
}
var components = URLComponents(string: urlPath)
if let queryString = string.components(separatedBy: "?").last {
components?.queryItems = []
let queryItems = queryString.components(separatedBy: "&")
for queryItem in queryItems {
guard let itemName = queryItem.components(separatedBy: "=").first,
let itemValue = queryItem.components(separatedBy: "=").last else {
continue
}
components?.queryItems?.append(URLQueryItem(name: itemName, value: itemValue))
}
}
return components!
}
// performRequest
func performRequest(inputURLString: String) {
// 1. Create URL
let components = transformURLString(inputURLString)
if let url = components?.url {
// 2. Create URLSession
let session = URLSession(configuration: .default)
// 3. Give the URLSession a task
let task = session.dataTask(with: url) {(data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(JSONobject: safeData)
}
}
// 4. Start the task
task.resume()
}
}
// parseJSON
func parseJSON(JSONobject: Data) {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([CoordinateData].self, from: JSONobject)
let name = decodedData[0].name
let lat = decodedData[0].lat
let lon = decodedData[0].lon
let coordinateModel = CoordinateModel(name: name, lat: lat, lon: lon)
let weatherURL = coordinateModel.urlString
// ------- WeatherManager fetchWeather ---------
weathetManager.fetchWeather(urlString: weatherURL)
} catch {
print(error)
}
}
}
CoordinateModel
import Foundation
struct CoordinateModel {
let name: String
let lat: Double
let lon: Double
// wheather 地理座標を元に検索した天気の情報
let weatherstr = "https://api.openweathermap.org/data/2.5/weather?"
let str = "units=metric&appid=c8e60c6317c653a1789294c00f54ae19#"
// computedproperty
var urlString: String {
return "\(weatherstr)&lat=\(lat)&lon=\(lon)&\(str)"
}
}
You create a weatherManager in the WeatherViewController , and in the viewDidLoad method you set the WeatherViewController as a delegate to the weatherManager instance you created here. In the CoordinateManager class, you create a new weatherManager, and don't set a delegate for it.
2 solutions, use what is most convenient for you:
Create an initializer for the CoordinateManager, to pass it the WeatherManager that you have created and that you are listening to
// CoordinateManager class
let weatherManager: WeatherManager
init(weatherManager: WeatherManager) {
self.weatherManager = weatherManager
}
// WeatherViewController class
let weatherManager = WeatherManager()
var coordinateManager = CoordinateManager(weatherManager: weatherManager)
You can directly set WeatherViewController as a delegate to the desired weatherManager instance.
// WeatherViewController class
override func viewDidLoad() {
super.viewDidLoad()
searchTextField.delegate = self
searchTextField.keyboardType = .asciiCapable
// Instead weatherManager.delegate = self
coordinateManager.weatherManager.delegate = self
}
I have 2 view models TransferViewModel which has the respective TransferViewController for making Local Transactions from a model LocalTransactionRequest and i have BankTransferViewModel which has a model BankTransactionsRequest, the first one is working but the second one is not, both view controllers are supposed to perform segue to another view controller ConfirmViewController, but the second one (BankTransferViewController) is not working
[This one is TransferViewController][1]
private func setupViewModel() {
viewModel.isTransfer = isTransfer
viewModel.loan = loan
viewModel.getBalance()
transferButton.rx.tap.asObservable().subscribe(onNext: { [weak self] _ in
guard let strongSelf = self else { return }
if let isVerified = UserManager.shared.get()?.IsVerified.value, isVerified{
strongSelf.viewModel.phoneNumberText.accept(strongSelf.phoneNumberTextField.text ?? "")
strongSelf.viewModel.amountText.accept(strongSelf.amountTextField.text ?? "")
strongSelf.viewModel.transfer()
}else{
strongSelf.showVerificationAlert()
}
}).disposed(by: disposeBag)
viewModel.accountInfo.asObservable().subscribe(onNext: { [weak self] accountInfo in
if let account = accountInfo{
guard let strongSelf = self else { return }
strongSelf.accountInfo = account
let request = LocalTransactionRequest(Identification: UserManager.shared.identification ?? "", Amount: Double(strongSelf.amountTextField.text!)!, ReceiverPhoneNumber: strongSelf.phoneNumberTextField.text!, IDBankAccount: UserManager.shared.defaultBankAccountId ?? -1, IsFromTransfer: strongSelf.isTransfer, Description: strongSelf.descriptionTF.text!)
strongSelf.transferRequest.accept(request)
strongSelf.performSegue(withIdentifier: "segue_toConfirmTransfer", sender: account)
}
}).disposed(by: disposeBag)
}
[This one is BankTransferViewController][2]
private func setupViewModel(){
viewModel.isTransfer = isTransfer
viewModel.getBalance()
transferButton.rx.tap.asObservable().subscribe(onNext: { [weak self] _ in
guard let strongSelf = self else { return }
if let isVerified = UserManager.shared.get()?.IsVerified.value, isVerified{
strongSelf.viewModel.bankNumberText.accept(strongSelf.bankNumberTextField.text ?? "")
strongSelf.viewModel.firstName.accept(strongSelf.firstNameTextField.text ?? "")
strongSelf.viewModel.lastName.accept(strongSelf.lastNameTextField.text ?? "")
strongSelf.viewModel.amountText.accept(strongSelf.amountTextField.text ?? "")
strongSelf.viewModel.descriptionText.accept(strongSelf.descriptionTF.text ?? "")
strongSelf.viewModel.transferNational()
}else{
strongSelf.showVerificationAlert()
}
}).disposed(by: disposeBag)
viewModel.transferRequest.asObservable().subscribe(onNext: { [weak self] bankRequest in
if let bank = bankRequest{
guard let strongSelf = self else { return }
strongSelf.bankTransferRequest = bank
let request = BankTransactionRequest(Identification: UserManager.shared.identification ?? "", ReceiverBankAccount: strongSelf.bankNumberTextField.text!, ReceiverFirst: strongSelf.firstNameTextField.text!, ReceiverLast: strongSelf.lastNameTextField.text!, Amount: Double(strongSelf.amountTextField.text!)!, Description: strongSelf.descriptionTF.text!)
strongSelf.nationalTransferRequest.accept(request)
DispatchQueue.main.async {
strongSelf.performSegue(withIdentifier: "segue_toConfirmTransfer", sender: bank)
}
}
}).disposed(by: disposeBag)
}
This is view model of BankTransferViewController
import RxCocoa
import RxSwift
class BankTransferViewModel: BaseViewModel {
private let transferUseCase: TransferUseCase
var accountInfo: BehaviorRelay<AccountExistModel?> = BehaviorRelay(value: nil)
var balance: BehaviorRelay<BalanceModel?> = BehaviorRelay(value: nil)
var bankNumberText: BehaviorRelay<String> = BehaviorRelay(value: "")
var firstName: BehaviorRelay<String> = BehaviorRelay(value: "")
var lastName: BehaviorRelay<String> = BehaviorRelay(value: "")
var amountText: BehaviorRelay<String> = BehaviorRelay(value: "")
var descriptionText: BehaviorRelay<String> = BehaviorRelay(value: "")
var transferRequest: BehaviorRelay<BankTransactionRequest?> = BehaviorRelay(value: nil)
var accountExist = PublishSubject<Bool>()
var hasMoney = PublishSubject<Bool>()
var invalidBankNumber = PublishSubject<Bool>()
var accountCannotRecieve = PublishSubject<Bool>()
var isTransfer : Bool = true
var transferPressed: AnyObserver<Void> {
return AnyObserver { [weak self] event in
switch event {
case .next:
guard let strongSelf = self else {
return
}
strongSelf.checkValidation()
default:
break
}
}
}
init(transferUseCase: TransferUseCase) {
self.transferUseCase = transferUseCase
}
func transferNational() {
self.checkValidation()
}
private func checkValidation() {
guard let balance = self.balance.value else {
state.onNext(.error(error: RepoError(with: "Dështoi verifikimi i disponueshmërisë financiare. Ju lusim të provoni më vonë.")))
return
}
if bankNumberText.value == ""{
state.onNext(.error(error: RepoError(with: "Plotëso fushën për numrin e bankës të pranuesit.")))
return
}
if bankNumberText.value.count < 6{
state.onNext(.error(error: RepoError(with:"Ju lutemi, shtypni një numër valid të gjirollogarisë")))
return
}
guard let doubleAmount = Double(amountText.value), doubleAmount > 0 else {
state.onNext(.error(error: RepoError(with: "Shuma jo e rregullt")))
return
}
if amountText.value == ""{
state.onNext(.error(error: RepoError(with: "Shuma jo e
saktë.")))
return
}
if balance.Balance < doubleAmount{
state.onNext(.error(error: RepoError(with: "Nuk keni fonde të mjaftueshme për realizimin e transaksionit.")))
return
}
if bankNumberText.value != "" && amountText.value != "" && (balance.Balance >= doubleAmount) {
// checkAccountExist()
}
}
func checkModulus16(accountNumber: String) -> Bool {
if accountNumber.isEmpty{
return false
}
let newValue = accountNumber.dropLast(2)
let mod = Int64(newValue + "00")! % 97
let result = 98 - mod
let derivedData = newValue + "" + (result < 10 ? "0\(result)" : "\(result)")
return Int64(derivedData) == Int64(accountNumber)
}
func showError(with message: String ) {
state.onNext(.error(error: RepoError(with: message)))
}
func getBalance(){
let params = ["Identification": UserManager.shared.identification ?? "" ] as ApiJson
transferUseCase.getBalance(with: params) {[weak self] (balance, error) in
guard let strongSelf = self else { return }
if let error = error {
strongSelf.state.onNext(.error(error: error))
strongSelf.accountExist.onNext(false)
}else if let balance = balance{
UserManager.shared.userBonus = balance.BonusAmount
strongSelf.state.onNext(.content)
strongSelf.balance.accept(balance)
strongSelf.accountExist.onNext(true)
UserManager.shared.updateBalance(with: balance)
}
}
}
//MARK: - baseViewModel
override func tryAgain() {
self.getBalance()
}
}
Make sure the following points are valid for your performSegue to work in BankTransferViewController:
The BankTransferViewController has a segue pointing to ConfirmViewController.
The identifier in your performSegue(withIdentifier: yourIdentifier, sender: yourModel) is the exact same identifier as the segue in storyboard that is connecting the two view controllers.
Since you are using it inside the viewModel.transferRequest.asObservable().subscribe(onNext: code, make sure you are emmiting a value to viewModel.transferRequest somewhere in the code. Otherwise, performSegue will never get called.
Since you have this check if let bank = bankRequest{ before using performSegue, make sure the transferRequest value you emmit is not nil.
I am working on a IPhone app that uses CoreML and ARKit simultaneously. The CoreML is supposed to recognize a number and the ARKit should detect a vertical plane (aka wall) and add some planes over that same wall with the content displayed on those planes depending on the recognised number.
So, the CoreML is working 100%. Everytime I "change" the number the topPrediction variable updates automatically ( so far so good ). The thing is that my variable in func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor does not update! What I mean is that the first number recognized by the CoreML is correctly sent to the renderer func and it works like a charm but if I turn the camera to another number it stills assumes that it's the first number! As you may see in the code, I even tried making a func getGabNum() -> Int and then calling it in the renderer func ( var num = getGabNum() ) but I continue getting the warning "Variable 'num' was never mutated; consider changing to 'let' constant" which means that something is not right. So guys, here is my code! Hope you can help me and thank you!
struct Room : Decodable {
let id : Int?
let num : Int?
//Adicionar Schedules
var horario = [Schedule]()
}
struct Schedule : Decodable {
let id : Int?
let hora_ini : Date?
let hora_fim : Date?
let descr : String?
private enum CodingKeys: String, CodingKey {
case id
case hora_ini
case hora_fim
case descr
}
}
class ViewController: UIViewController, ARSCNViewDelegate {
#IBOutlet weak var debugLabel: UILabel!
#IBOutlet weak var debugTextView: UITextView!
#IBOutlet weak var sceneView: ARSCNView!
let dispatchQueueML = DispatchQueue(label: "com.hw.dispatchqueueml") // A Serial Queue
var visionRequests = [VNRequest]()
var room: Room?
var room_array: [[Int]] = [[17, 0], [43, 0], [120,0]]
var teste = 0
var num = -1
override func viewDidLoad() {
super.viewDidLoad()
sceneView.delegate = self
sceneView.showsStatistics = true
let scene = SCNScene()
sceneView.scene = scene
configureLighting()
guard let selectedModel = try? VNCoreMLModel(for: SalasMLv6().model) else {
fatalError("Could not load model.")
}
let classificationRequest = VNCoreMLRequest(model: selectedModel, completionHandler: classificationCompleteHandler)
classificationRequest.imageCropAndScaleOption = VNImageCropAndScaleOption.centerCrop // Crop from centre of images and scale to appropriate size.
visionRequests = [classificationRequest]
loopCoreMLUpdate()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
setUpSceneView()
}
func setUpSceneView()
{
let configuration = ARWorldTrackingConfiguration()
configuration.planeDetection = .vertical
sceneView.session.run(configuration)
sceneView.debugOptions = [ARSCNDebugOptions.showFeaturePoints]
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
sceneView.session.pause()
}
func configureLighting()
{
sceneView.automaticallyUpdatesLighting = true
sceneView.autoenablesDefaultLighting = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
// MARK: - ARSCNViewDelegate
func renderer(_ renderer: SCNSceneRenderer, updateAtTime time: TimeInterval) {
DispatchQueue.main.async {
// Do any desired updates to SceneKit here.
}
}
func loopCoreMLUpdate() {
dispatchQueueML.async {
self.updateCoreML()
self.loopCoreMLUpdate()
}
}
func updateCoreML() {
// Get Camera Image as RGB
let pixbuff : CVPixelBuffer? = (sceneView.session.currentFrame?.capturedImage)
if pixbuff == nil { return }
let ciImage = CIImage(cvPixelBuffer: pixbuff!)
// Prepare CoreML/Vision Request
let imageRequestHandler = VNImageRequestHandler(ciImage: ciImage, options: [:])
// Run Vision Image Request
do {
try imageRequestHandler.perform(self.visionRequests)
} catch {
print(error)
}
}
func classificationCompleteHandler(request: VNRequest, error: Error?) {
// Catch Errors
if error != nil {
print("Error: " + (error?.localizedDescription)!)
return
}
guard let observations = request.results else {
print("No results")
return
}
// Get Classifications
let classifications = observations[0...2] // top 3 results
.compactMap({ $0 as? VNClassificationObservation })
.map({ "\($0.identifier) \(String(format:" : %.2f", $0.confidence))" })
.joined(separator: "\n")
// Render Classifications
DispatchQueue.main.async {
// Display Debug Text on screen
self.debugTextView.text = "TOP 3 PROBABILITIES: \n" + classifications
// Display Top Symbol
var symbol = "❌"
var gabNum: Int?
let topPrediction = classifications.components(separatedBy: "\n")[0]
let topPredictionName = topPrediction.components(separatedBy: ":")[0].trimmingCharacters(in: .whitespaces)
// Only display a prediction if confidence is above 90%
let topPredictionScore:Float? = Float(topPrediction.components(separatedBy: ":")[1].trimmingCharacters(in: .whitespaces))
if (topPredictionScore != nil && topPredictionScore! > 0.05) {
if (topPredictionName == "120") {
symbol = "1️⃣2️⃣0️⃣"
gabNum = 120
self.teste = gabNum!
}
if (topPredictionName == "43") {
symbol = "4️⃣3️⃣"
gabNum = 43
self.teste = gabNum!
}
if (topPredictionName == "17") {
symbol = "1️⃣7️⃣"
gabNum = 17
self.teste = gabNum!
}
}
if let gn = gabNum {
// get room from REST
let jsonURL = "someURL\(gn)"
guard let url = URL(string: jsonURL) else {
return
}
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil{
print("error)")
return
}
do {
self.room = try JSONDecoder().decode(Room.self, from: data!)
}catch{
print(“Decoder Error”)
}
}.resume()
}
self.debugLabel.text = symbol
}
}
// MARK: - HIDE STATUS BAR
override var prefersStatusBarHidden : Bool { return true }
func getGabNum() -> Int {
return self.teste
}
func renderer(_ renderer: SCNSceneRenderer, didAdd node: SCNNode, for anchor: ARAnchor)
{
guard room != nil else {
print("room == nil")
return
}
guard let planeAnchor = anchor as? ARPlaneAnchor else {
return
}
num = getGabNum()
if( num == room_array[0][0] && room_array[0][1] == 1 ){
return
}else{
if( num == room_array[1][0] && room_array[1][1] == 1 ){
return
}else{
if( num == room_array[2][0] && room_array[2][1] == 1 ){
return
}else{
var i = 0
for horario in (self.room?.horario)!{
// Planes and Nodes Stuff Right Here
}
switch self.room?.num{
case 17: room_array[0][1] = 1
case 43: room_array[1][1] = 1
case 120:room_array[2][1] = 1
}
}
}
}
}
}
We are running into a really strange issue with Realm (Swift). If we retrieve an entry immediately after writing it to the db it is completely different from the written one. What could possible cause this. Below is a screenshot from the debugger where it happens. The code to fetch the entry looks like this:
func fetchEntry(assetID: String) throws -> UploadInfo? {
let realm = try Realm()
return realm.objects(UploadInfo.self).filter { $0.assetID == assetID }.first
}
The entry class looks like this:
class UploadInfo: Object {
override static func primaryKey() -> String? {
return "assetID"
}
override static func indexedProperties() -> [String] {
return ["sessionID", "taskID"]
}
#objc dynamic var sessionID: String = ""
#objc dynamic var taskID: Int = 0
#objc dynamic var totalBytesSent: Int64 = 0
#objc dynamic var totalBytesExpectedToSend: Int64 = 0
#objc dynamic var downloadProgress: Float = 0.0
#objc dynamic var uploadProgress: Float = 0.0
#objc dynamic var fileURLPath: String = "" // path of file that is uploaded (i.e. multipart body)
#objc dynamic var path: String = ""
#objc dynamic var assetID: String = "" //PHAsset localIdentifier
#objc dynamic var fileModificationTime: DegooTimestamp = 0
#objc dynamic var fileSize: Int64 = 0 // unprocessedTotalFileDataLength
#objc dynamic var fileChecksumBase64: String = ""
#objc dynamic var fileChecksum: Data = Data(base64Encoded: "")!
var fileURL: URL {
return URL(fileURLWithPath: fileURLPath)
}
func makeCopy() -> UploadInfo {
let ui = UploadInfo()
ui.sessionID = sessionID
ui.taskID = taskID
ui.totalBytesSent = totalBytesSent
ui.totalBytesExpectedToSend = totalBytesExpectedToSend
ui.downloadProgress = downloadProgress
ui.uploadProgress = uploadProgress
ui.fileURLPath = fileURLPath
ui.path = path
ui.assetID = assetID
ui.fileModificationTime = fileModificationTime
ui.fileSize = fileSize
ui.fileChecksumBase64 = fileChecksumBase64
ui.fileChecksum = fileChecksum
return ui
}
}
extension UploadInfo {
var data: Data? {
do {
guard try FileManager.default.isFile(at: self.fileURL) else { return nil }
return try Data(contentsOf: self.fileURL)
} catch {
Log.error(error)
return nil
}
}
var asset: PHAsset? {
return PHAsset.fetchAssets(withLocalIdentifiers: [self.assetID], options: nil).firstObject
}
var shouldBeOptimized: Bool {
guard let asset = self.asset else { return false }
return ImageOptimizer.default.shouldBeReoptimized(width: asset.pixelWidth, height: asset.pixelHeight)
}
func cleanUpUrls() {
for url in urlsToCleanUp {
do {
if FileManager.default.fileExists(atPath: url.absoluteString) {
try FileManager.default.removeItem(at: url)
}
} catch {
Log.error(error)
}
}
}
var urlsToCleanUp: [URL] {
let localAssetPathHelper = LocalAssetPathHelper()
let assetFileName = localAssetPathHelper.fileName(for: URL(fileURLWithPath: self.path))
let multipartBodyFileName = localAssetPathHelper.fileName(for: self.fileURL)
return [
URL(fileURLWithPath: localAssetPathHelper.path(for: multipartBodyFileName)),
URL(fileURLWithPath: localAssetPathHelper.path(for: "/Photos/".appending(assetFileName))),
URL(fileURLWithPath: localAssetPathHelper.path(for: "/Videos/".appending(assetFileName)))
]
}
}
We have tried this on both Realm 3.1.1 and 3.5.0.