Is it possible to add an Image to a CGFloat - uiimage

I am trying to create a progress bar that goes from 0%-100%. When the progress reaches 100%, I would like it to fade from the percentage to an image such as a check mark. I am setting this progress bar up as a CAShapeLayer and inside of it, I would like the the image to appear after it reaches 100. I see this animation done throughout various apps and I'm wondering if it is possible to do in Xcode. I have tried to do this by converting an image to a CAShapeLayer but it fails with a compiler error:
Cannot convert value of type 'UIImage?' to expected argument type 'String'
I have used the code below, but it only shows the percentage of loading.
is it possible to be done?
private func beginDownloadingFile() {
print("Attempting to download file")
shapeLayer.strokeEnd = 0
let configuration = URLSessionConfiguration.default
let operationQueue = OperationQueue()
let urlSession = URLSession(configuration: configuration, delegate: self, delegateQueue: operationQueue)
guard let url = URL(string: urlString) else { return }
let downloadTask = urlSession.downloadTask(with: url)
downloadTask.resume()
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {
let percentage = CGFloat(totalBytesWritten) / CGFloat(totalBytesExpectedToWrite)
DispatchQueue.main.async {
self.percentageLabel.text = "\(Int(percentage * 100))%"
self.shapeLayer.strokeEnd = percentage
AudioServicesPlaySystemSound(SystemSoundID(kSystemSoundID_Vibrate))
}
print(percentage)
}
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
print("Finished downloading file")
}
Thank you for explaining whether or not this can be accomplished in advance.

First, the reason for getting the error message Cannot convert value of type 'UIImage?' to expected argument type 'String':
This is because in the last line (UIImage(named: tick)) in your snippet you try to initialize an UIImage with the UIImage constant tick you initialized two lines earlier.
What you try to accomplish is possible, but the way you try it will not work.
To help you with that, you should show how your UI is set up.
A possible solution:
Embed your percentageLabel in a UIStackView. Add a UIImageView to the stackview too. Set this ImageView to show the desired image and set the isHidden property of the imageView to true.
When your progress reaches 100%, do the following:
percentageLabel.isHidden = true
imageView.isHidden = false

Related

Image on tabItem, UIKit UITabBarController

Hi im making an app with uikit and i want to set a profile image(received from url) on Tabbar item. I am expecting the updated profile image on TabBarItem. Also i want to add a border around the image when the tab is selected.
I found this solution, but the image does not appear in the tabitem.
Set user's profile picture on Tabbar item
extension UITabBarController {
func addSubviewToLastTabItem(_ image: UIImage) {
if let lastTabBarButton = self.tabBar.subviews.last, let tabItemImageView = lastTabBarButton.subviews.first {
if let accountTabBarItem = self.tabBar.items?.last {
accountTabBarItem.selectedImage = nil
accountTabBarItem.image = nil
}
let imgView = UIImageView()
imgView.frame = tabItemImageView.frame
imgView.layer.cornerRadius = tabItemImageView.frame.height/2
imgView.layer.masksToBounds = true
imgView.contentMode = .scaleAspectFill
imgView.clipsToBounds = true
imgView.image = image
self.tabBar.subviews.last?.addSubview(imgView)
}
}
}
Could someone help me to do that?
Did you add the second part of code:
let yourImage = UIImage(named: "your-image")
override func viewDidLayoutSubviews() { // add to any vc
super.viewDidLayoutSubviews()
tabBarController?.addSubviewToLastTabItem(yourImage)
}
When you call addSubviewToLastTabItem("String") it is critical print correctly parameter without mistakes.
Could you post this part of code?
One more thing viewDidLayoutSubviews will work if you rotate iPhone, double check please that you are calling addSubviewToLastTabItem() in viewDidLoad()

I have an audio player that starts on my home screen

Whenever I go back to my home screen it replays the music over the already playing music. I tried making an if statements that 'obviously' doesn't work (because I barely know any swift!). Here is my home screen code:
var myAudioPlayer = AVAudioPlayer()
override func viewDidLoad() {
let myFilePathString =
NSBundle.mainBundle().pathForResource("16 March of the Resistance", ofType: "m4a")
if let myFilePathString = myFilePathString
{
let myFilePathURL = NSURL(fileURLWithPath: myFilePathString)
do{
try myAudioPlayer = AVAudioPlayer(contentsOfURL: myFilePathURL)
myAudioPlayer.play()
}catch
{
print("error")
}
}
}
How can I stop it from playing on top of itself?
Here is a picture of my storyboard
enter image description here
The music player is on StartScreen, but when I click the back button, it starts another music player over the current one.
I need code which says
is myAudioPlayer playing?
if yes do not play song again
else play "16 March of the resistance"
enter image description here
You need to check if you are already playing. You can do this by the playing property of the AVAudioPlayer class.
Guard your viewDidLoad with say:
if !myAudioPlayer.playing
Note that I don't know any Swift.
I'm not sure what you mean by "my home screen", is it the device or your main VC? Why are you overriding viewDidLoad()? A previous player could never be playing in the viewDidLoad() call, unless for a leak which is what's needing fixing.
Otherwise, you just need to declare your player as an optional, rather than creating it in the declaration. As for the replaying or overlapping, just check if you player exists, although you wouldnt do this in your viewDidLoad(). ViewDidLoad() is called when the view of the VC is created and the view loaded, which doesn't relate to your lifecycle of the audio.
Depending on your requirements, you might want to create the player somewhere else that is plays throughout the app, rather than restarting when visiting that VC. Otherwise the code below will avoid the duplication. The problem was this var myAudioPlayer = AVAudioPlayer() which would duplicate players and one wasn't being released.
var myAudioPlayer: AVAudioPlayer?
func viewDidLoad() {
// this should never be needed with correct player setup
if let player = myAudioPlayer where player.playing {
return
}
if let pathString = NSBundle.mainBundle().pathForResource("16 March of the Resistance", ofType: "m4a")
{
let url = NSURL(fileURLWithPath: pathString)
do{
try myAudioPlayer = AVAudioPlayer(contentsOfURL: url)
myAudioPlayer?.play()
} catch {
print("error")
}
}
}
Update
So there is no need to make the VC do this. Create a Class of e.g. AudioController, which could even be a singleton. Initialise it in your AppDelegate and trigger this player setup code after initialisation. Your view controllers dont need to know about the audio controller. IF e.g. you do want to modify the volume from a setting page, you just access the singleton object and set its volume.
This way you keep all your audio controller code nicely separated. Dont just think of functionality, think of overall software architecture, with objects having clear responsibilities. Keep your ViewControllers light, they shouldnt be doing much processing.
Take a look at this Singletons , its really easy to setup a singleton in Swift nowadays.
class AudioController {
static let sharedInstance = AudioController()
var audioPlayer: AVAudioPlayer?
let kVolumeKey = "VolumeKey"
let kHasSavedInitialVolumeKey = "HasSavedInitialVolumeKey"
var volume: Float = 0.5
func setup() {
self.loadVolume()
self.setupPlayer()
}
func updatePlayerVolume(volume: Float) {
self.audioPlayer?.volume = volume
self.volume = volume
self.saveVolume()
}
func saveVolume() {
NSUserDefaults.standardUserDefaults().setBool(true, forKey: kHasSavedInitialVolumeKey)
NSUserDefaults.standardUserDefaults().setFloat(self.volume, forKey: kVolumeKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
func loadVolume() {
if NSUserDefaults.standardUserDefaults().boolForKey(kHasSavedInitialVolumeKey) {
self.volume = NSUserDefaults.standardUserDefaults().floatForKey(kVolumeKey)
}
}
func setupPlayer() {
if let pathString = NSBundle.mainBundle().pathForResource("16 March of the Resistance", ofType: "m4a")
{
let url = NSURL(fileURLWithPath: pathString)
do{
try audioPlayer = AVAudioPlayer(contentsOfURL: url)
audioPlayer?.volume = self.volume
audioPlayer?.play()
} catch {
print("error")
}
}
}
}
So, you can just setup the player from your AppDelegate's didFinishLaunching
AudioController.sharedInstance.setupPlayer()
And then you can access the AudioController using AudioController.sharedInstance.whatever when you need to get or set. Also, you will need to implement the players delegate methods for handling the end of the song, possibly triggering a new song to play from a playlist(create a new object for this) or whatever. Your player delegate code is also nicely separated now.
Looking at your storyboard it seems that when you click the Back button on the second ViewController you allocate another start screen and you push it on the screen.
It seems that you're using a UINavigationController if so when the back button is tapped you should call:
self.navigationController?.popViewControllerAnimated(true)
If your showing it as a modal you should dismiss is like this:
self.dismissViewControllerAnimated(true, completion: nil)

This application is modifying the autolayout engine from a background thread swift2.0

I am using this simple code to extract some plain text from a website.
#IBAction func askWeather(sender: AnyObject) {
let url = NSURL(string: "http://www.weather-forecast.com/locations/" + userField.text! + "/forecasts/latest")!
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) -> Void in
if let urlContent = data{
let webContent = NSString(data: urlContent, encoding: NSUTF8StringEncoding)
let wArray = webContent?.componentsSeparatedByString("Day Weather Forecast Summary:</b><span class=\"read-more-small\"><span class=\"read-more-content\"> <span class=\"phrase\">")
let wCont = wArray![1].componentsSeparatedByString("</span>")
self.weatherResult.text = wCont[0]
}
else{
print("Sorry could not get weather information")
}
}
task.resume()
}
#IBOutlet var weatherResult: UILabel!
#IBOutlet var userField: UITextField!
And after i press the button to fetch the information nothing happens for several seconds(like 10-20) and then i get the correct result however i get this message in xcode:
This application is modifying the autolayout engine from a background
thread, which can lead to engine corruption and weird crashes. This will cause an exception in a future release.
I tried reading some posts on others having this problem but they were using threads like async etc. to run their code. Im not really sure what the problem is in my case.
Thank you!
I'm guessing that self.weatherResult.text = wCont[0] is modifying something like a UILabel or similar, in which case you're trying to change part of your user interface from a background thread – a big no-no.
Try code like this instead:
dispatch_async(dispatch_get_main_queue()) { [unowned self] in
self.weatherResult.text = wCont[0]
}
let task = NSURLSession.sharedSession().dataTaskWithURL(url) {(data, response, error) -> Void in
There's your asynchronous thread. Right there. dataTaskWithURL runs in the background and will eventually call the callback function that you passed in. And that is done in the background.

How to read Finder icons (left source list) on OS X using Swift

I try to read information about icons that are shown in finder on left source list. I tried already NSFileManager with following options
NSURLEffectiveIconKey icon read is not the same as in finder
NSURLCustomIconKey - returns nil
NSURLThumbnailKey - returns nil
NSThumbnail1024x1024SizeKey - returns nil
I managed to read all mounted devices using NSFileManager but I have no clue how to read icons connected with devices? Maybe someone has any idea or a hint.
I also tried to use
var image: NSImage = NSWorkspace.sharedWorkspace().iconForFile((url as! NSURL).path!)
but it returns the same image as NSURLEffectiveIconKey
Thanks!
First, the proper way to query which volumes are shown in the Finder's sidebar is using the LSSharedFileList API. That API also provides a way to query the icon:
LSSharedFileListRef list = LSSharedFileListCreate(NULL, kLSSharedFileListFavoriteVolumes, NULL);
UInt32 seed;
NSArray* items = CFBridgingRelease(LSSharedFileListCopySnapshot(list, &seed));
CFRelease(list);
for (id item in items)
{
IconRef icon = LSSharedFileListItemCopyIconRef((__bridge LSSharedFileListItemRef)item);
NSImage* image = [[NSImage alloc] initWithIconRef:icon];
// Do something with this item and icon
ReleaseIconRef(icon);
}
You can query other properties of the items using LSSharedFileListItemCopyDisplayName(), LSSharedFileListItemCopyResolvedURL, and LSSharedFileListItemCopyProperty().
This answer is a translation to Swift 1.2 of Ken Thomases's Objective-C answer.
All credits go to Ken Thomases, this is just a translation of his awesome answer.
let listBase = LSSharedFileListCreate(kCFAllocatorDefault, kLSSharedFileListFavoriteVolumes.takeUnretainedValue(), NSMutableDictionary())
let list = listBase.takeRetainedValue() as LSSharedFileList
var seed:UInt32 = 0
let itemsCF = LSSharedFileListCopySnapshot(list, &seed)
if let items = itemsCF.takeRetainedValue() as? [LSSharedFileListItemRef] {
for item in items {
let icon = LSSharedFileListItemCopyIconRef(item)
let image = NSImage(iconRef: icon)
// use image ...
}
}
Explanations:
When translating Ken's answer from Objective-C to try and use it I encountered some difficulties, this is the reason why I made this answer.
First problem was with LSSharedFileListCreate, the method signature in Swift didn't accept nil as its first parameter. I had to find a constant representing a CFAllocator: kCFAllocatorDefault. And the third parameter didn't accept nil either, so I put a dummy unused NSMutableDictionary to keep the compiler happy.
Also the "seed" parameter for LSSharedFileListCopySnapshot didn't accept the usual var seed:Uint32? for the inout, I had to give a default value to seed.
For deciding when to use takeRetainedValue or takeUnRetainedValue when using these APIs I referred to this answer.
Last, I had to cast the returned array as a Swift array of LSSharedFileListItemRef elements (it was initially inferred as a CFArray by the compiler).
Update
This has been deprecated in OS X El Capitan 10.11 (thanks #patmar)
Update 2
Note that while it's been deprecated it still works. The cast as [LSSharedFileListItemRef] in the previous solution is now ignored so we have to cast as NSArray instead then cast the item later:
if let items = itemsCF.takeRetainedValue() as? NSArray {
for item in items {
let icon = LSSharedFileListItemCopyIconRef(item as! LSSharedFileListItem)
let image = NSImage(iconRef: icon)
// use image ...
}
}
NSURLCustomIconKey will return nil because support for this key is not implemented. It's mentioned in the header but not in the NSURL documentation. You could get the info via deprecated File Manager methods.
https://developer.apple.com/library/mac/documentation/Carbon/Reference/File_Manager/
Alternatively maybe something like this.
func getResourceValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>,
forKey key: String,
error error: NSErrorPointer) -> Bool
Parameters
value
The location where the value for the resource property identified by key should be stored.
key
The name of one of the URL’s resource properties.
error
The error that occurred if the resource value could not be retrieved. This parameter is optional. If you are not interested in receiving error information, you can pass nil.
2022, swift 5
low-res icon (really fast work):
let icon: NSImage = NSWorkspace.shared.icon(forFile: url.path )
Hi-res icon:
extension NSWorkspace {
func highResIcon(forPath path: String, resolution: Int = 512) -> NSImage {
if let rep = self.icon(forFile: path)
.bestRepresentation(for: NSRect(x: 0, y: 0, width: resolution, height: resolution), context: nil, hints: nil) {
let image = NSImage(size: rep.size)
image.addRepresentation(rep)
return image
}
return self.icon(forFile: path)
}
}
Also hi-res thumbnail:
fileprivate extension URL {
func getImgThumbnail(_ size: CGFloat) -> NSImage? {
let ref = QLThumbnailCreate ( kCFAllocatorDefault,
self as NSURL,
CGSize(width: size, height: size),
[ kQLThumbnailOptionIconModeKey: false ] as CFDictionary
)
guard let thumbnail = ref?.takeRetainedValue()
else { return nil }
if let cgImageRef = QLThumbnailCopyImage(thumbnail) {
let cgImage = cgImageRef.takeRetainedValue()
return NSImage(cgImage: cgImage, size: CGSize(width: cgImage.width, height: cgImage.height))
}
return nil
}
}

Swift: Error updating UI component when using a delegate

I'm trying to update a progress bar with the progress of loading a load of values into CoreData. However, whenever I try to call an update on my progressView component, I get a fatal error stating that "unexpectedly found nil while unwrapping an Optional value".
The interesting thing is that this happens even if I put 'self.progressView.progress = 0.5' in the delegate method of my program - indicating that it's the progressView component it can't find rather than an issue with the value. A quick check with println also confirms the value does exist and so isn't nil. Note that if I put the 'self.progressView.progress = 0.5' statement under a function connected directly to a button, it works fine so it must be some sort of issue with the command being called from the delegate.
Can anyone work out what I'm doing wrong here? Thanks for your help.
Delegate method:
class ViewControllerUpdate: UIViewController, NSURLSessionDelegate, NSURLSessionDownloadDelegate, saveUpdate {
[....]
func updateStatus(status: String, progress: Float?) {
if let percentProgress = progress? {
self.progressView.progress = 0.5
}
//println(progress) - NOTE THIS IS CORRECTLY POPULATED WITH THE APPROPRIATE VALUE
}
Calling class:
protocol saveUpdate {
func updateStatus(status:String, progress:Float?)
}
class sqlPullSave {
let classtoUpdate: saveUpdate = ViewControllerUpdate()
func saveTSVtoSQL(fromFile: NSURL) -> Int {
//Load up the information into a Dictionary (tsv)
//let tsvURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(fromFileName, ofType: fromFileExtension)!)
let tsvURL: NSURL = fromFile
let tab = NSCharacterSet(charactersInString: "\t")
let tsv = CSV(contentsOfURL: tsvURL, separator: tab)
//let defResult: AnyObject = tsv.rows[0]["Name"]!
//let tryagain:String = AnyObjecttoString(tsv.rows[1]["Name"]!)
//load the data into the SQLite database...
dispatch_async(dispatch_get_main_queue()) {
for a in 0..<tsv.rows.count {
self.SQLsaveLine(self.AnyObjecttoString(tsv.rows[a]["Name"]!),
name_l: "",
desc: self.AnyObjecttoString(tsv.rows[a]["1"]!),
jobTitle: self.AnyObjecttoString(tsv.rows[a]["2"]!),
extn: self.AnyObjecttoString(tsv.rows[a]["3"]!)
// update status
var percentComplete: Float = (Float(a) / Float(tsv.rows.count))
self.classtoUpdate.self.updateStatus("SQLload", progress: percentComplete)
}
}
return 0
}

Resources