Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Building Functional Apps in Swift for iOS & OS X

Building Functional Apps in Swift for iOS & OS X

Sam Soffes

May 21, 2015
Tweet

More Decks by Sam Soffes

Other Decks in Programming

Transcript

  1. enum RedactionType: Int, Printable { case Pixelate, Blur, BlackBar var

    description: String { switch self { case .Pixelate: return string("PIXELATE") case .Blur: return string("BLUR") case .BlackBar: return string("BLACK_BAR") } } static var all: [RedactionType] { return [.Pixelate, .Blur, .BlackBar] } }
  2. private func bundle() -> NSBundle? { return NSBundle(forClass: RedactedView.self) }

    func string(key: String) -> String { if let bundle = bundle() { return bundle.localizedStringForKey(key, value: nil, table: nil) } return key }
  3. func filter(image: CIImage, preprocessor: Preprocessor = Redaction.preprocess) -> CIFilter {

    let extent = image.extent() let scaledRect = rectForBounds(extent).flippedInRect(extent) let processed = preprocessor(image: image, type: type) return CIFilter(name: "CISourceOverCompositing", withInputParameters: [ "inputImage": processed.imageByCroppingToRect(scaledRect) ]) }
  4. static func preprocess(image: CIImage, type: RedactionType) -> CIImage { switch

    type { case .Pixelate: return // ... case .Blur: return // ... case .BlackBar: return // ... } }
  5. func filter(image: CIImage, preprocessor: Preprocessor = Redaction.preprocess) -> CIFilter {

    let extent = image.extent() let scaledRect = rectForBounds(extent).flippedInRect(extent) let processed = preprocessor(image: image, type: type) return CIFilter(name: "CISourceOverCompositing", withInputParameters: [ "inputImage": processed.imageByCroppingToRect(scaledRect) ]) }
  6. class RedactionsController { var redactions = [Redaction]() var image: UIImage?

    { didSet { if let image = image { ciImage = CIImage(CGImage: image.CGImage) } else { ciImage = nil } } } private var ciImage: CIImage? { didSet { updateImages() } } // ... }
  7. private var pixelatedImage: CIImage? private var blurredImage: CIImage? private func

    updateImages() { if let ciImage = ciImage { pixelatedImage = Redaction.preprocess(ciImage, type: .Pixelate) blurredImage = Redaction.preprocess(ciImage, type: .Blur) } else { pixelatedImage = nil blurredImage = nil } }
  8. func process() -> CIImage? { if let ciImage = ciImage

    { var outputImage = ciImage if redactions.count > 0 { let chain = ChainFilter() chain.inputImage = ciImage chain.inputFilters = redactions.map({ $0.filter(ciImage, preprocessor: self.preprocess) }) outputImage = chain.outputImage! } return outputImage.imageByCroppingToRect(ciImage.extent()) } return nil }
  9. private func preprocess(image: CIImage, type: RedactionType) -> CIImage { switch

    type { case .Pixelate: return pixelatedImage! case .Blur: return blurredImage! default: return Redaction.preprocess(image, type: type) } }
  10. private let redactionsController = RedactionsController() public var redactions: [Redaction] {

    get { return redactionsController.redactions } set { redactionsController.redactions = newValue updateRedactions() } }
  11. func select(redaction: Redaction) { if isSelected(redaction) { return } selectedUUIDs.insert(redaction.UUID)

    CATransaction.begin() CATransaction.setDisableActions(true) let box = BoundingBoxLayer() boundingBoxes[redaction.UUID] = box let layer: CALayer? = self.layer layer?.addSublayer(box) updateSelections() CATransaction.commit() }
  12. var selectedRedactions: [Redaction] { var selected = [Redaction]() let allUUIDs

    = redactions.map({ $0.UUID }) for UUID in selectedUUIDs { if let index = find(allUUIDs, UUID) { selected.append(redactions[index]) } } return selected }
  13. func updateSelections() { CATransaction.begin() CATransaction.setDisableActions(true) for redaction in selectedRedactions {

    if let layer = boundingBoxes[redaction.UUID] { var rect = redaction.rectForBounds(imageRect) rect.inset(dx: -2, dy: -2) layer.frame = rect } } CATransaction.commit() NSNotificationCenter.defaultCenter().postNotificationName(... }
  14. public func drag(#point: CGPoint, state: GestureRecognizerState) { if image ==

    nil { return } let point = convertPointToUnits(point) // Begin dragging if state == .Began { deselectAll() // Start moving if let redaction = hitTestRedaction(point) { draggingMode = .Moving(redaction.UUID, redaction.rect, point) select(redaction) } // Start creating else { let redaction = Redaction(type: mode, rect: CGRect(origin: point, size: .zeroSize)) draggingMode = .Creating(redaction.UUID) redactions.append(redaction) select(redaction) } }
  15. // Continue dragging if let draggingMode = draggingMode { switch

    draggingMode { case let .Creating(UUID): // Update size based on origin and current point case let .Moving(UUID, rect, startPoint): // Update rect based on original rect and start point } } // End dragging if state == .Ended { draggingMode = nil } }
  16. #if os(iOS) import UIKit.UIFont public typealias FontType = UIFont #else

    import AppKit.NSFont public typealias FontType = NSFont #endif public typealias Font = FontType
  17. #if os(iOS) import UIKit.UIColor public typealias ColorType = UIColor #else

    import AppKit.NSColor public typealias ColorType = NSColor extension NSColor { public convenience init(red: CGFloat, green: CGFloat, blue: CGFloat, alpha: CGFloat) { self.init(SRGBRed: red, green: green, blue: blue, alpha: alpha) } } #endif public typealias Color = ColorType
  18. #if os(iOS) import UIKit.UIImage public typealias ImageType = UIImage #else

    import AppKit.NSImage public typealias ImageType = NSImage extension NSImage { public var CGImage: CGImageRef! { return CGImageForProposedRect(nil, context: nil, hints: nil)?.takeUnretainedValue() } // Optional to match UIImage public convenience init?(CGImage cgImage: CGImageRef) { self.init(CGImage: cgImage, size: CGSizeZero) } } #endif public typealias Image = ImageType
  19. #if os(iOS) extension Image { public convenience init?(named name: String,

    inBundle bundle: NSBundle?) { self.init(named: name, inBundle: bundle, compatibleWithTraitCollection: nil) } } #endif // Mac version implemented in Objective-C
  20. public class View: ViewType { #if os(iOS) public var wantsLayer:

    Bool { set { // Do nothing } get { return true } } #else public override func viewDidMoveToWindow() { didMoveToWindow() } public func didMoveToWindow() { super.viewDidMoveToWindow() }