UICollectionView snap scrolling and pagination

:  ~ 2 min read

This is a pretty straightforward snapping logic for a collection view with cells of same size, and one section (logic for more sections can be easily added).

scrollViewWillEndDragging has an inout targetContentOffset parameter, so we can read, calculate and modify the end position of the scroll. Luckily, we don't need to take into consideration insets, line or item spacing (lost a lot of time including them, not understanding why the correct math produces wrong results), but we do need to consider the case where the user scrolls past the last cell - the targetContentOffset will be within bounds, but the […]


Continue reading →

Writing is hard

:  ~ 40 sec read

I think that what I do here can't really be called writing; it's more like scribbling, my posts are rather small and rare. But I try to be consistent, even if what I'm going to write about seems to not be of a big deal, because I believe that any info, no matter how big or small, might turn out to be useful to someone.

On the other hand, despite all these facts, I have weeks when I have absolutely no idea what to write about; there's zero inspiration. I can't even imagine how hard it is to consistently write […]


Continue reading →

NSDate operators

:  ~ 40 sec read

I personally find this a bit of a mouthful, especially if you have to type it a lot:

if someDate.compare(otherDate) == .OrderedAscending {
  // Do stuff
}

But we can have a few operators to make our lives a bit easier:

func <(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs.compare(rhs) == .OrderedAscending
}
func <=(lhs: NSDate, rhs: NSDate) -> Bool {
    return lhs < rhs || lhs == rhs
}

func >(lhs: […]

Continue reading →

Detecting retain cycles and improved logging

:  ~ 40 sec read

I think adding a deinit method, with a print statement everywhere is a decent first barrier against retain cycles:

deinit {
  print("Object X has been deinitialized.")
}

This way, if you expect object x to deinit at some point, but it doesn't, you at least know you need to start searching.

Now, for the improved printing, to make this a bit cleaner and easier:

func customPrint(
  object: Any,
  function: String = #function,
  _ file: String = #file,
  _ line: UInt = #line) {
  #if DEBUG […]

Continue reading →

Improving UIFont workflow

:  ~ 40 sec read

Usually an app has fonts with well defined purposes. So why not let enums make our lives easier, a little bit? First, a couple of them, to define our font families and weights:

struct Font {
  private enum Family: String {
    case AvenirNext
    case ProximaNova
  }

  private enum Weight: String {
    case Regular
    case Medium
    case DemiBold
    case Bold
  }
}

Then a method to easily create fonts:

private static func baseFont(family family: Family, size: CGFloat, weight: Weight = .Regular, italic: Bool = false) -> UIFont { […]

Continue reading →

Setting variables with tuples, switches and closures

:  ~ 1 min read

Let's say we have a custom UILabel, which in turn has several types; maybe a StatusLabel that can be of type sold out and expired. The label would have several common properties, but each type would have something specific. How can we go about this?

class StatusLabel: UILabel {
  enum Type {
    case SoldOut
    case Expired
  }

  init(type: Type) {
    super.init(frame: .zero)

    font = UIFont.commonFont()
    layer.cornerRadius = 4
    textAlignment = .Center
    translatesAutoresizingMaskIntoConstraints = false
  }
}

We now covered […]


Continue reading →

Better interaction between viewWillTransitionToSize and CGSize

:  ~ 25 sec read

Instead of checking if size.width > size.height, we can have three handy CGSize extensions:

extension CGSize {
  var isCompact: Bool { return height > width + delta }
  var isWide: Bool { return width > height + delta }
  var isSquare: Bool { return abs(width - height) < delta } 
}

For usage within viewWillTransitionToSize I don't think the delta will be really needed, but if we want to use these properties for our own custom views, it might come in handy. Modify its value to fit your own needs, of course.

The MAS, updates and the CLI

:  ~ 52 seconds read

I've had problems with stuck updates, or slow downloads with the MAS for as far as I can remember. softwareupdate never really was of much help, using MAS' Debug menu neither, nor killing softwareupdate related processes.

Yesterday I found the answer to all of this: a gem for manipulating the MAS from the CLI. It uses native APIs, from login (the MAS login pops up), to downloading files (you can even start an update with the MAS and finish it on the CLI - the download files are the same). And you also get a nice, little progress bar.

A […]


Continue reading →

Easier hugging / compression handling

:  ~ 1 min read

I'm pretty sure this won't suit all cases, but, usually, a label / button should highly resist being vertically shrunk more than its intrinsic size. On the other hand, we won't always mind if it grows larger than its intrinsic size, but we'd like to avoid it, if possible.

I, personally, find this a bit of a mouthful:

label.setContentCompressionResistancePriority(UILayoutPriorityRequired, forAxis: .Vertical)
label.setContentHuggingResistancePriority(UILayoutPriorityDefaultHigh, forAxis: .Vertical)

So, let's extract them into a method, with default values, as added bonus, and use an enum, as extra […]


Continue reading →

TableViews, collectionViews and Swift enums

:  ~ 40 sec read

I talked about how we can have a safer and cleaner tableView / collectionView section handling, but we can improve it even further, with protocol extensions:

protocol Countable {
  var rawValue: Int { get }
  static var count: Int { get }
  init?(rawValue: Int)
}
extension Countable {
  static var count: Int {
    var max = 0
    while let _ = self.init(rawValue: max) { max += 1 }
    return max
  }
}

Then, when we create an Int extension, we have the number of cases, out of […]


Continue reading →