Saturday, June 01, 2019

100 Days of Swift

3. For a tougher challenge, take the image generation code out of cellForRowAt: generate all images when the app first launches, and use those smaller versions instead. For bonus points, combine the getDocumentsDirectory() method I introduced in project 10 so that you save the resulting cache to make sure it never happens again.

As a reminder, here’s the code for getDocumentsDirectory():
func getDocumentsDirectory() -> URL {
    let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
    return paths[0]
}

The above is from Day 98 of Paul Hudson's excellent course: 100 Days of Swift. It is Challenge #3 and there is even a "bonus" challenge inside the challenge! In approaching the solution--before coding--I wrote down my thoughts and then gathered snippets of code that I thought would help. My notes are below. I thought it might be helpful for others to share them without giving away the solution.

First, I jotted down the four main things I was concerned with:
  1. Read big images from bundle, see project 1.
  2. Render a image into a rect using CoreGraphics like in project 30. 
  3. Read the thumbnails in like project 10
  4. If a thumbnail does not exist then create it. Also like in project 10.
Next, I gathered the corresponding snippets of the code:

From project 1:
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
    let fm = FileManager.default
    let path = Bundle.main.resourcePath!
    let items = try! fm.contentsOfDirectory(atPath: path)
    
    let pictureNames = items
        .filter { $0.hasPrefix("nssl") }
        .sorted { $0 < $1 }
    
    self?.storms = pictureNames.map {
        Storm(name: $0, imageName: $0) }
    
    DispatchQueue.main.async { [weak self] in
        self?.collectionView.reloadData()
    }
}

From project 30:
        let path = Bundle.main.path(forResource: imageRootName, ofType: nil)!
        let original = UIImage(contentsOfFile: path)!

        let renderRect = CGRect(origin: .zero, size: CGSize(width: 90, height: 90))
        let renderer = UIGraphicsImageRenderer(size: renderRect.size)
        
        let rounded = renderer.image { ctx in
            ctx.cgContext.addEllipse(in: renderRect)
            ctx.cgContext.clip()
            
            original.draw(in: renderRect)
        }

From project 10:

Write:
        let imageName = UUID().uuidString
        let imagePath = getDocumentsDirectory().appendingPathComponent(imageName)
        if let jpegData = image.jpegData(compressionQuality: 0.8) {
            try? jpegData.write(to: imagePath)
        }

Read:
        let path = getDocumentsDirectory().appendingPathComponent(person.imageName)
        cell.imageView.image = UIImage(contentsOfFile: path.path)

Wednesday, December 31, 2014

The Ash Entity Framework


I’ve been doing object-oriented programming for over 20 years. I’ve worked with many languages in that time. One can improve their ability to abstract the more they work with different languages. A more challenging exercise is to change your programming paradigm. 

Lately, the paradigm I’ve been experimenting with is entity component systems (ECS). ECS are touted for game programming. A common scenario in games is for things in the game to changes powers and abilities. To solve this issue in the past, I’ve relied heavily on composition over inheritance. In ECS, that idea is taken to the extreme.

The objects (entities) are made up of components (that are mostly value objects), the logic resides in the systems that operate on the entities. After reading an excellent post by Richard Lord, I decided to give his ECS micro framework a try, it’s call Ash.  

The Ash Entity Framework

The Ash Entity Framework uses entity, components, systems, and nodes. Ash facilities the work of mapping the systems to the components they work on. Let me define these:
  • Components are (mostly) simple value objects. They contain little or no behavior.
  • Entities hold components. Entities only care about components. By changing what components an entity has affects which systems affect the entity. This changing of entity composition is important as it impacts node creation as well as which systems execute on it. Entity names must be unique! 
  • Systems operate on lists of nodes. Systems only care about nodes. Systems contain the logic. 
  • Nodes are combinations of one or more components. Node classes are defined and at runtime, an entity that has components matching the definition gets a node created by the Ash engine. Ash also removes and destroys nodes when they are no longer applicable to the entity composition. Node lists use signals--not events--for adding and removing.
The Engine is part of the framework. It knows about entities and systems, both of which you add to the engine. It in turn watches entities and--as they change--adds and removes the components to node collections used by systems. The engine creates the node collections and passes them to the appropriate systems.


Wednesday, February 12, 2014

ABGC On Sale

My course A Blender Game Character is on sale at CartoonSmart thru Saturday, $25 (usually $39) #b3d

Sunday, December 29, 2013

Bits of Blender #67 - Text Boxes

Did you know you could use text boxes/frames in Blender? John and Richard show you how in this Bit.

 

Sunday, December 22, 2013

Bits of Blender #66 - Object Fonts

Do you know how to create your own fonts using Blender objects? This Bit shows you how.


Monday, December 16, 2013

Bits of Blender #65 - Typography



It's the Bits of Blender Christmas edition! In it we show you how to do some typographic tricks using nodes.

Sunday, December 08, 2013

How to play Astra Itinera


I'm long overdue with this tutorial on how to play my iPad game Astra Itinera. If you've questions you can post here, contact me directly, or post in the facebook group.