UICollectionViewCell snapshot - uicollectionviewcell

[code]
I need to take a snapshot of a collectionview cell for the provided indexpath, i have created the below function and it always returns image as nil
Please suggest me what am doing wrong here,
func screenshotForCellAtIndexPath(indexPath: IndexPath!, rect: CGRect!) -> UIImage?
{
let cellRect = rect
UIGraphicsBeginImageContextWithOptions(cellRect!.size, false, 0.0)
guard let context = UIGraphicsGetCurrentContext() else { return nil }
let cell = collectionView.cellForItem(at: indexPath)
cell?.layer.render(in: context)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
[code]

Taking a snapshot of Collection view cell for selected indexpath.
Try this for taking snapshot of
var yourImage: UIImage?
UIGraphicsBeginImageContextWithOptions(yourView.bounds.size, false, UIScreen.main.scale)
yourView.drawHierarchy(in: myView.bounds, afterScreenUpdates: true)
yourImage = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return yourImage!

Related

SwiftUI: What's 'Escaping closure captures mutating 'self' parameter' and how to fix it

I try to compare the current date to dates I've safed on Firebase. I convert them form timestamp to dates before comparing. And if the equal, I want to set the value of bool1 (variable) to true, in order to use it later to set a background color.
By doing so, I get the Error:
Escaping closure mutating self parameter.
Thats the code:
struct RKDate1 {
var date: Date
let rkManager: RKManager1
#State var bool1 = false
var isDisabled: Bool = false
var isToday: Bool = false
var issafed: Bool = false
var isSelected: Bool = false
var isBetweenStartAndEnd: Bool = false
init(date: Date, rkManager: RKManager1, isDisabled: Bool, isToday: Bool, isSelected: Bool, isBetweenStartAndEnd: Bool) {
self.date = date
self.rkManager = rkManager
self.isDisabled = isDisabled
self.isToday = isToday
self.isSelected = isSelected
self.isBetweenStartAndEnd = isBetweenStartAndEnd
let db = Firestore.firestore()
db.collection("Termin").order(by: "date").addSnapshotListener { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges{
let id = i.document.documentID
let date1 = i.document.get("date") as! Timestamp
let components = Calendar.current.dateComponents([.day,.month, .year], from: date1.dateValue())
let components1 = Calendar.current.dateComponents([.day,.month, .year], from: date)
let currentdate = Calendar.current.date(from: components1)
let newdate = Calendar.current.date(from: components)
if newdate == currentdate {
self.bool1 = true
}
}
}
}
}
EDIT:
Seems like you cannot mutate structs anymore in escaping closure without removing #escaping which not be possible in your case. You might want to consider changing your implementation to a class.
Structs are immutable. Which mean they cannot be mutated. In your case you are modifying the value of self.bool1 = true which is changing the value of self.
~~A better way (IMO) would be to create a mutating func to do your firebase call and update the values inside mutating function.~~
Also, you shouldn’t use State property wrappers in models. They should only be used in views.
Thank you for the response. I appreciate your efforts. I tried with a mutating func, sadly it isn't working either. I get the same error message. I didn't used a State property anymore. I'm a beginner and not quite sure whether I have fully understood what I have might have done wrong.
mutating func finddates() {
let db = Firestore.firestore()
db.collection("Termin").order(by: "date").addSnapshotListener { (snap, err) in
if err != nil{
print((err?.localizedDescription)!)
return
}
for i in snap!.documentChanges{
let id = i.document.documentID
let date1 = i.document.get("date") as! Timestamp
let components = Calendar.current.dateComponents([.day,.month, .year], from: date1.dateValue())
let components1 = Calendar.current.dateComponents([.day,.month, .year], from: self.date)
let currentdate = Calendar.current.date(from: components1)
let newdate = Calendar.current.date(from: components)
if newdate == currentdate {
self.bool1 = true
}
}
}
}

Change colour of UImage from firebase to black and white

Is it possible to load an image from firebase using a url and then changing it's color to black and white?
If I used an image from assets it works but I'm loading my images from firebase via a url and when I try to apply the code below backgroundProfileImage.image equals nil or an Image is not detected. Im also using SDWebImage load my images if that helps. How can I make this work? I Appreciate the help in advance. Thanks
func ChangeImageColor(){
//Auto Adjustment to Input Image
var inputImage = CIImage(image: self.backgroundProfileImage.image!) //backgroundProfileImage is not
let options:[String : AnyObject] = [CIDetectorImageOrientation:1 as AnyObject]
let filters = inputImage!.autoAdjustmentFilters(options: options)
for filter: CIFilter in filters {
filter.setValue(inputImage, forKey: kCIInputImageKey)
inputImage = filter.outputImage
}
let cgImage = self.context.createCGImage(inputImage!, from: inputImage!.extent)
self.backgroundProfileImage.image = UIImage(cgImage: cgImage!)
//Apply noir Filter
let currentFilter = CIFilter(name: "CIPhotoEffectTonal")
currentFilter!.setValue(CIImage(image: UIImage(cgImage: cgImage!)), forKey: kCIInputImageKey)
let output = currentFilter!.outputImage
// let cgimg = self.context.createCGImage(output!, from: output!.extent)
let processedImage = UIImage(cgImage: cgimg)
self.backgroundProfileImage.image = processedImage
}
})
}
This is how I'm retrieving the image
self.backgroundProfileImage.sd_setImage(with: URL(string: backgroundProfileImageURL), placeholderImage: UIImage(named: "empty-profile-3.png"))
Ok this method works for me
func ChangeImageColor(){
let currentFilter = CIFilter(name: "CIPhotoEffectNoir")
currentFilter!.setValue(CIImage(image: self.backgroundProfileImage.image!), forKey: kCIInputImageKey)
let output = currentFilter!.outputImage
let cgimg = context.createCGImage(output!,from: output!.extent)
let processedImage = UIImage(cgImage: cgimg!)
self.backgroundProfileImage.image = processedImage
}
imgView.sd_setImage(with: URL(string:url), completed: { (image, error, type, url) in
//Do any thing with image here.
self.ChangeImageColor(image)
})
func ChangeImageColor(_ image: UIImage){
//Auto Adjustment to Input Image
var inputImage = CIImage(image: image)
let options:[String : AnyObject] = [CIDetectorImageOrientation:1 as AnyObject]
let filters = inputImage!.autoAdjustmentFilters(options: options)
for filter: CIFilter in filters {
filter.setValue(inputImage, forKey: kCIInputImageKey)
inputImage = filter.outputImage
}
let cgImage = self.context.createCGImage(inputImage!, from: inputImage!.extent)
self.backgroundProfileImage.image = UIImage(cgImage: cgImage!)
//Apply noir Filter
let currentFilter = CIFilter(name: "CIPhotoEffectTonal")
currentFilter!.setValue(CIImage(image: UIImage(cgImage: cgImage!)), forKey: kCIInputImageKey)
let output = currentFilter!.outputImage
// let cgimg = self.context.createCGImage(output!, from: output!.extent)
let processedImage = UIImage(cgImage: cgimg)
self.backgroundProfileImage.image = processedImage
}
})
}
Call the method when you retrieve the image with your library and pass it as parameter.

video plays in wrong collection view cell

I have a collection view that displays a video in each cell. It also contains a play button and an image which is displayed before the video is played. When I scroll up and down it theres no problem, but when I play a video it shows in the right cell but sometimes it also displays in another cell when I scroll down. I've tried using DispatchQueue(not sure if its the right thing to do) and that doesn't work so I'm stuck for ideas. I have another similar collection view that shows just images and it works perfectly but I'm having issues with this collection view because it displays videos instead. I've searched other questions but I can't find the answer I need to solve this issue. Help would much appreciated. Thanks in advance!
Collection view controller
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: homePostCellId, for: indexPath) as! videoListCollectionViewCell
cell.photoImageView.sd_setImage(with: URL(string: posts[indexPath.item].imageUrl!), placeholderImage: UIImage(named: “placeholder-image.png"))
let tapGesture : UITapGestureRecognizer = UITapGestureRecognizer.init(target: self, action: #selector(playVideo))
return cell
}
}
collection view Cell
class videoListCollectionViewCell: UICollectionViewCell {
var post: videoPost?
lazy var playButton: UIButton = {
let button = UIButton(type: .system)
button.translatesAutoresizingMaskIntoConstraints = false
let image = UIImage(named: "playButton7")
button.tintColor = UIColor.white
button.setImage(image, for: UIControlState())
button.addTarget(self, action: #selector(handlePlay), for: .touchUpInside)
return button
}()
lazy var asset: AVURLAsset = {
let videoUrlString = self.post?.videoUrl
let url = URL(string: videoUrlString!)
var asset: AVURLAsset = AVURLAsset(url: url!)
asset.resourceLoader.setDelegate(self, queue: DispatchQueue.main)
return asset
}()
var playerLayer: AVPlayerLayer?
var player: AVPlayer?
var observer:Any!
func handlePlay() {
// The video has been download already to the document directory
let filename = self.post?.postID
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(filename!)?.path
let fileManager = FileManager.default
let RealURL = NSURL(fileURLWithPath: filePath!)
if fileManager.fileExists(atPath: filePath!) {
player = AVPlayer(url: RealURL as URL)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = photoImageView.bounds
photoImageView.layer.addSublayer(playerLayer!)
player?.play()
playButton.isHidden = true
print("Playing from saved disk")
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
} else {
// The video hasn't been downloaded so it's loading from the URL
if let videoUrlString = post?.videoUrl, let url = URL(string: videoUrlString) {
player = AVPlayer(url: url)
playerLayer = AVPlayerLayer(player: player)
playerLayer?.frame = photoImageView.bounds
photoImageView.layer.addSublayer(playerLayer!)
player?.play()
activityIndicatorView.startAnimating()
playButton.isHidden = true
print("Attempting to play video")
self.observer = self.player?.addPeriodicTimeObserver(forInterval: CMTimeMake(1, 600), queue: DispatchQueue.main) {
[weak self] time in
if self?.player?.currentItem?.status == AVPlayerItemStatus.readyToPlay {
if (self?.player?.currentItem?.isPlaybackLikelyToKeepUp) != nil {
self?.activityIndicatorView.stopAnimating()
}
}
}
NotificationCenter.default.addObserver(self, selector:#selector(self.playerDidFinishPlaying(note:)),name: NSNotification.Name.AVPlayerItemDidPlayToEndTime, object: player?.currentItem)
}
}
}
func playerDidFinishPlaying(note: NSNotification){
print("Video Stopped”)
self.player?.pause()
playButton.isHidden = false
let exporter = AVAssetExportSession(asset: asset, presetName: AVAssetExportPresetHighestQuality)
let filename = self.post?.postID
let documentsDirectory = FileManager.default.urls(for: FileManager.SearchPathDirectory.documentDirectory, in: FileManager.SearchPathDomainMask.userDomainMask).last!
let archiveURL = documentsDirectory.appendingPathComponent(filename!)
exporter?.outputURL = archiveURL
exporter?.outputFileType = AVFileTypeMPEG4
let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let url = NSURL(fileURLWithPath: path)
let filePath = url.appendingPathComponent(filename!)?.path
let fileManager = FileManager.default
if fileManager.fileExists(atPath: filePath!) {
} else {
exporter?.exportAsynchronously(completionHandler: {
print(exporter?.status.rawValue)
print(exporter?.error)
})
}
}

Firebase deleted data still there

i have a behavior that I can't understand.
I delete a node on firebase database and I still receive the data during observing .value. But in firebase database the node is deleted.
I have a node called users_shoppinglists. Here are all id's of the users nodes to observe stored. Then I iterate all the id's to observe and call a function that observes each ID.
When I need to delete a list I update a node called status on the shoppinglists node and delete all to this list related data via cloud functions.
But the data is still received during observe. It seems I receive the data again before it is completely deleted.
Iterate all id's:
func ObserveAllList() -> Void{
if currentUser == nil { return }
self.ShowActivityIndicator()
ref.child("users_shoppinglists").child(currentUser!.id!).observe(.value, with: { (usersListsSnap) in
if usersListsSnap.value is NSNull { self.HideActivityIndicator(); return }
for listSnap in usersListsSnap.children {
let list = listSnap as! DataSnapshot
self.ObserveSingleList(listID: list.key)
}
}) { (error) in
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
}
}
Call function to observe each ID:
func ObserveSingleList(listID:String) -> Void {
self.ShowActivityIndicator()
ref.child("shoppinglists").child(listID).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.value is NSNull { self.HideActivityIndicator(); return }
//Read listData
var newList = ShoppingList()
newList.id = snapshot.key
newList.name = snapshot.childSnapshot(forPath: "listName").value as? String
newList.owneruid = snapshot.childSnapshot(forPath: "owneruid").value as? String
newList.relatedStore = snapshot.childSnapshot(forPath: "relatedStore").value as? String
//Read List items
self.ref.child("listItems").child(listID).observe(.value, with: { (itemSnap) in
var newItems = [ShoppingListItem]()
for items in itemSnap.children {
let item = items as! DataSnapshot
var newItem = ShoppingListItem()
newItem.id = item.key
newItem.listID = listID
newItem.isSelected = item.childSnapshot(forPath: "isSelected").value as? Bool
newItem.itemName = item.childSnapshot(forPath: "itemName").value as? String
newItem.sortNumber = item.childSnapshot(forPath: "sortNumber").value as? Int
newItems.append(newItem)
}
newList.items = newItems
//Read List members
self.ref.child("shoppinglist_member").child(listID).observe(.value, with: { (memberSnap) in
var newMembers = [ShoppingListMember]()
for members in memberSnap.children {
let member = members as! DataSnapshot
var m = ShoppingListMember()
m.memberID = member.key
m.status = member.value as? String
newMembers.append(m)
}
newList.members = newMembers
DispatchQueue.main.async {
if let index = allShoppingLists.index(where: { $0.id == listID }){
allShoppingLists[index] = newList
} else {
allShoppingLists.append(newList)
}
self.HideActivityIndicator()
NotificationCenter.default.post(name: Notification.Name.ShoppingBuddyListDataReceived, object: nil, userInfo: nil)
}
}, withCancel: { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
})
}, withCancel: { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
})
}) { (error) in
self.HideActivityIndicator()
NSLog(error.localizedDescription)
let title = String.OnlineFetchRequestError
let message = error.localizedDescription
self.ShowAlertMessage(title: title, message: message)
return
}
}
Cloud function:
//****************************************************************************************************************/
// Handles an action when status value changed in users_shoppinglists node
//****************************************************************************************************************/
exports.handle_ListStatusUpdate = functions.database.ref('/shoppinglists/{listID}').onUpdate(event => {
var listData = event.data.val()
console.log('Status', listData.status)
//handle deleted by owner
if (String(listData.status) == 'deleted by owner') {
//Get all members to delete the list on their users_shoppinglists node
return admin.database().ref('shoppinglist_member').child(event.params.listID).once('value').then(listMember => {
var promises = []
listMember.forEach(function (member) {
promises.push(admin.database().ref('users_shoppinglists').child(member.key).child(event.params.listID).set(null).then(() => {
return admin.database().ref('shoppinglist_member').child(event.params.listID).set(null).then(() => {
// delete the original shopping list
return admin.database().ref('shoppinglists').child(event.params.listID).set(null).then(() => {
return admin.database().ref('listItems').child(event.params.listID).set(null).then(() => {
})
})
})
}))
})
})
}
});/*********************************************************************************************************** */
Had this issue on Simulator. It was not only .value but .childRemoved and .childChanged were not triggered at all (only .childAdded was working).
Tried on iPhone and it worked. Then I made "Erase All Content And Settings..." to Simulator and it started to work again on Simulator too.
My bet is that firebase cache gets dirty during development, while you add or remove observers in code and probably change structure in database and at some point it just stops reacting appropriately.

Swift code to use NSOutlineView as file system directory browser

I'm struggling with this Swift code already for some time and do not find the problem. The code
below should provide the File Directory as DataSource for a NSOutlineView. The GUI is quite simple
just a window with a NSOutlineView and a Object for the OutlineViewController instance.
When I start the application it shows the root entry, when I expand the root entry it shows for a short period the sub items. Then the application crashes with an Error in file "main.swift" at line "NSApplicationMain(C_ARGC, C_ARGV) --> "EXC_BAD_ACCESS(code=EXC_I386_GPFLT)" ?
If added some println() to proof the directory structure - this seems to be fine.
The swift code:
import Cocoa
import Foundation
class FileSystemItem {
let propertyKeys = [NSURLLocalizedNameKey, NSURLEffectiveIconKey, NSURLIsPackageKey, NSURLIsDirectoryKey,NSURLTypeIdentifierKey]
let fileURL: NSURL
var name: String! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLNameKey], error: nil)
return resourceValues[NSURLNameKey] as? NSString
}
var localizedName: String! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLLocalizedNameKey], error: nil)
return resourceValues[NSURLLocalizedNameKey] as? NSString
}
var icon: NSImage! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLEffectiveIconKey], error: nil)
return resourceValues[NSURLEffectiveIconKey] as? NSImage
}
var dateOfCreation: NSDate! {
let resourceValues = self.fileURL.resourceValuesForKeys([NSURLCreationDateKey], error: nil)
return resourceValues[NSURLCreationDateKey] as? NSDate
}
var dateOfLastModification: NSDate! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLContentModificationDateKey], error: nil)
return resourceValues[NSURLContentModificationDateKey] as? NSDate
}
var typeIdentifier: String! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLTypeIdentifierKey], error: nil)
return resourceValues[NSURLTypeIdentifierKey] as? NSString
}
var isDirectory: String! {
let resourceValues = fileURL.resourceValuesForKeys([NSURLIsDirectoryKey], error: nil)
return resourceValues[NSURLIsDirectoryKey] as? NSString
}
var children: [FileSystemItem] {
var childs: [FileSystemItem] = []
var isDirectory: ObjCBool = ObjCBool(1)
let fileManager = NSFileManager.defaultManager()
var checkValidation = NSFileManager.defaultManager()
if (checkValidation.fileExistsAtPath(fileURL.relativePath)) {
if let itemURLs = fileManager.contentsOfDirectoryAtURL(fileURL, includingPropertiesForKeys:propertyKeys, options:.SkipsHiddenFiles, error:nil) {
for fsItemURL in itemURLs as [NSURL] {
if (fileManager.fileExistsAtPath(fsItemURL.relativePath, isDirectory: &isDirectory))
{
if(isDirectory == true) {
let checkItem = FileSystemItem(fileURL: fsItemURL)
childs.append(checkItem)
}
}
}
}
}
return childs
}
init (fileURL: NSURL) {
self.fileURL = fileURL
}
func hasChildren() -> Bool {
return self.children.count > 0
}
}
class OutlineViewController : NSObject, NSOutlineViewDataSource {
let rootFolder : String = "/"
let rootfsItem : FileSystemItem
let fsItemURL : NSURL
let propertyKeys = [NSURLLocalizedNameKey, NSURLEffectiveIconKey, NSURLIsPackageKey, NSURLIsDirectoryKey,NSURLTypeIdentifierKey]
init() {
self.fsItemURL = NSURL.fileURLWithPath(rootFolder)
self.rootfsItem = FileSystemItem(fileURL: fsItemURL)
for fsItem in rootfsItem.children as [FileSystemItem] {
for fsSubItem in fsItem.children as [FileSystemItem] {
println("\(fsItem.name) - \(fsSubItem.name)")
}
}
}
func outlineView(outlineView: NSOutlineView!, numberOfChildrenOfItem item: AnyObject!) -> Int {
if let theItem: AnyObject = item {
let tmpfsItem: FileSystemItem = item as FileSystemItem
return tmpfsItem.children.count
}
return 1
}
func outlineView(outlineView: NSOutlineView!, isItemExpandable item: AnyObject!) -> Bool {
if let theItem: AnyObject = item {
let tmpfsItem: FileSystemItem = item as FileSystemItem
return tmpfsItem.hasChildren()
}
return false
}
func outlineView(outlineView: NSOutlineView!, child index: Int, ofItem item: AnyObject!) -> AnyObject! {
if let theItem: AnyObject = item {
let tmpfsItem: FileSystemItem = item as FileSystemItem
return tmpfsItem.children[index]
}
return rootfsItem
}
func outlineView(outlineView: NSOutlineView!, objectValueForTableColumn tableColumn: NSTableColumn!, byItem item: AnyObject!) -> AnyObject! {
if let theItem: AnyObject = item {
let tmpfsItem: FileSystemItem = item as FileSystemItem
return tmpfsItem.localizedName
}
return "-empty-"
}
}
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet var window: NSWindow
func applicationDidFinishLaunching(aNotification: NSNotification?) {
// Insert code here to initialize your application
}
func applicationWillTerminate(aNotification: NSNotification?) {
// Insert code here to tear down your application
}
}
Any hints ?
I had a similar problem with EXC_BAD_ACCESS on an NSOutlineView - with an NSOutlineViewDataSource. The same behaviour of as soon as the node was expanded, the data was displayed then the crash occurred. Some profiling in instruments showed that somewhere a Zombie object was created, and then the Outline view tried to access it.
I think this is a bug - but I managed to get around it by changing all Swift 'Strings' to 'NSStrings'. This may have to be done for all Swift types if you are using them.
In order to ensure everything was an NSString, I had to declare constants within the class such as:
var empty_string : NSString = ""
Because anytime I fed it a Swift string all hell broke loose. Oh well hopefully this will be fixed in the future!
So, just to clarify what is going on. NSOutlineView does not retain objects that it is given for its "model"; it was always expected that the client would retain them. For ARC code, this doesn't work well, because if you return a new instance to the NSOutlineView methods the object will not be retained by anything and will quickly be freed. Then subsequent outlineView delegate methods the touch these objects will lead to crashes. The solution to that is to retain the objects yourself in your own array.
Note that the objects returned from objectValueForTableColumn are retained by the NSControl's objectValue.
Back to Swift: As Thomas noted the objects have to be objc objects since they are bridged to an objc class. A Swift string is implicitly bridged to a temporary NSString. This leads to a crash because of the above issue, since nothing retains the NSString instance. That is why maintaining an array of NSStrings "solves" this problem.
The solution would be for NSOutlineView to have an option to retain the items given to it. Please consider logging a bug request for it to do this through bugreporter.apple.com
Thanks,
corbin (I work on NSOutlineView)
It seems that
outlineView(outlineView: NSOutlineView!, objectValueForTableColumn tableColumn: NSTableColumn!, byItem item: AnyObject!) -> AnyObject!
needs to return an object that conforms to obj-c protocol. So you can return
#objc class MyClass {
...
}
(or NSString and the like). But not native Swift stuff like String or Array etc.
I believe one of the problems going on here is the fact that the "children" array is getting replaced every time the children property is accessed.
I think this causes some weak references inside the NSOutlineView to break when it queries the DataSource for information.
If you cache the "children" and access the cache to compute "numberOfChildren" and "getChildForIndex" you should see an improvement.
In Swift 3.0 I used the following code, which compiles and runs without problems. It is far away from being complete but a step in the right direction, since I am trying to translate TreeTest into Swift.
import Cocoa
import Foundation
class FileSystemItem: NSObject {
let propertyKeys: [URLResourceKey] = [.localizedNameKey, .effectiveIconKey, .isDirectoryKey, .typeIdentifierKey]
var fileURL: URL
var name: String! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.nameKey])
return resourceValues.name
}
var localizedName: String! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.localizedNameKey])
return resourceValues.localizedName
}
var icon: NSImage! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.effectiveIconKey])
return resourceValues.effectiveIcon as? NSImage
}
var dateOfCreation: Date! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.creationDateKey])
return resourceValues.creationDate
}
var dateOfLastModification: Date! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.contentModificationDateKey])
return resourceValues.contentAccessDate
}
var typeIdentifier: String! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.typeIdentifierKey])
return resourceValues.typeIdentifier
}
var isDirectory: Bool! {
let resourceValues = try! fileURL.resourceValues(forKeys: [.isDirectoryKey])
return resourceValues.isDirectory
}
init(url: Foundation.URL) {
self.fileURL = url
}
var children: [FileSystemItem] {
var childs: [FileSystemItem] = []
let fileManager = FileManager.default
// show no hidden Files (if you want this, comment out next line)
// let options = FileManager.DirectoryEnumerationOptions.skipsHiddenFiles
var directoryURL = ObjCBool(false)
let validURL = fileManager.fileExists(atPath: fileURL.relativePath, isDirectory: &directoryURL)
if (validURL && directoryURL.boolValue) {
// contents of directory
do {
let childURLs = try
fileManager.contentsOfDirectory(at: fileURL, includingPropertiesForKeys: propertyKeys, options: [])
for childURL in childURLs {
let child = FileSystemItem(url: childURL)
childs.append(child)
}
}
catch {
print("Unexpected error occured: \(error).")
}
}
return childs
}
func hasChildren() -> Bool {
return self.children.count > 0
}
}
class OutLineViewController: NSViewController, NSOutlineViewDelegate, NSOutlineViewDataSource {
#IBOutlet weak var outlineView: NSOutlineView!
#IBOutlet weak var pathController: NSPathControl!
var fileSystemItemURL: URL!
let propertyKeys: [URLResourceKey] = [.localizedNameKey, .effectiveIconKey, .isDirectoryKey, .typeIdentifierKey]
var rootfileSystemItem: FileSystemItem!
var rootURL: URL!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let userDirectoryURL = URL(fileURLWithPath: NSHomeDirectory())
// directory "Pictures" is set as root
let rootURL = userDirectoryURL.appendingPathComponent("Pictures", isDirectory: true)
self.pathController.url = rootURL
self.rootfileSystemItem = FileSystemItem(url: rootURL)
for fileSystemItem in rootfileSystemItem.children as [FileSystemItem] {
for subItem in fileSystemItem.children as [FileSystemItem] {
print("\(fileSystemItem.name) - \(subItem.name)")
}
}
//FileSystemItem.rootItemWithPath(self.pathControl.URL.path)
//self.searchForFilesInDirectory(picturesPath)
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
#IBAction func pathControllerAction(_ sender: NSPathControl) {
print("controller clicked")
}
// MARK: - outline data source methods
func outlineView(_ outlineView: NSOutlineView, numberOfChildrenOfItem item: Any?) -> Int {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.children.count
}
return 1
}
func outlineView(_ outlineView: NSOutlineView, isItemExpandable item: Any) -> Bool {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.hasChildren()
}
return false
}
func outlineView(_ outlineView: NSOutlineView, child index: Int, ofItem item: Any?) -> Any {
if let fileSystemItem = item as? FileSystemItem {
return fileSystemItem.children[index]
}
return rootfileSystemItem
}
func outlineView(_ outlineView: NSOutlineView, objectValueFor tableColumn: NSTableColumn?, byItem item: Any?) -> Any? {
if let fileSystemItem = item as? FileSystemItem {
switch tableColumn?.identifier {
case "tree"?:
return fileSystemItem.localizedName
case "coordinate"?:
return " empty "
default:
break
}
}
return " -empty- "
}
// MARK: - outline view delegate methods
func outlineView(_ outlineView: NSOutlineView, shouldEdit tableColumn: NSTableColumn?, item: Any) -> Bool {
return false
}
}
With a new edit the outline view now shows all files and directories. You can influence the appearance in the children section in class FileSystemItem.

Resources