         return round(measurement * UIScreen.mainScreen().scale) / UIScreen.mainScreen().scale
     }
     
-    func layoutKeys(model: Keyboard, bounds: CGRect, page pageToLayout: Int) {
+    func layoutKeys(model: Keyboard, bounds: CGRect, page pageToLayout: Int) -> [Key:CGRect]? {
         if bounds.height == 0 || bounds.width == 0 {
-            return
-        }
+            return nil
+        }
+        
+        var keyMap = [Key:CGRect]()
         
         let isLandscape: Bool = {
             let boundsRatio = bounds.width / bounds.height

                return self.rounded(returnWidth)
                }()
            
            for (r, row) in enumerate(page.rows) {
                let rowGapCurrentTotal = (r == page.rows.count - 1 ? rowGapTotal : CGFloat(r) * rowGap)
                let frame = CGRectMake(rounded(sideEdges), rounded(topEdge + (CGFloat(r) * keyHeight) + rowGapCurrentTotal), rounded(bounds.width - CGFloat(2) * sideEdges), rounded(keyHeight))
                
                // basic character row: only typable characters
                if self.characterRowHeuristic(row) {
                    self.layoutCharacterRow(row, modelToView: self.modelToView, keyWidth: letterKeyWidth, gapWidth: keyGap, frame: frame)
                }
                    
                    // character row with side buttons: shift, backspace, etc.
                else if self.doubleSidedRowHeuristic(row) {
                    self.layoutCharacterWithSidesRow(row, frame: frame, isLandscape: isLandscape, keyWidth: letterKeyWidth, keyGap: keyGap)
                }
                    
                    // bottom row with things like space, return, etc.
                else {
                    self.layoutSpecialKeysRow(row, modelToView: self.modelToView, keyWidth: letterKeyWidth, gapWidth: lastRowKeyGap, leftSideRatio: lastRowLeftSideRatio, rightSideRatio: lastRowRightSideRatio, micButtonRatio: self.layoutConstants.micButtonPortraitWidthRatioToOtherSpecialButtons, isLandscape: isLandscape, frame: frame)
                }
            }
        }
    }
    
    func characterRowHeuristic(row: [Key]) -> Bool {

    
    // TODO: temp
    func layoutTemp(pageNum: Int) {
        if var keyMap = self.layoutKeys(self.model, bounds: self.superview.bounds, page: pageNum) {
            CATransaction.begin()
            CATransaction.setDisableActions(true)
            
            self.modelToView?.removeAll(keepCapacity: true)
            self.viewToModel?.removeAll(keepCapacity: true)
            
            self.resetKeyPool()
            
            // TODO: move elsewhere
            for keyView in self.keyPool {
                keyView.hidePopup()
                keyView.hidden = true
                keyView.highlighted = false
            }
            
            let setupKey = { (view: KeyboardKey, model: Key, frame: CGRect) -> Void in
//                view.text = "?"
                view.hidden = false
                view.frame = frame
                self.modelToView?[model] = view
                self.viewToModel?[view] = model
            }
            
            var foundCachedKeys = [Key]()
            
            // pass 1: get cached keys
            for (key, frame) in keyMap {
                if let keyView = self.pooledKey(frame) {
                    foundCachedKeys.append(key)
                    setupKey(keyView, key, frame)
                }
            }
            
            foundCachedKeys.map {
                keyMap.removeValueForKey($0)
            }
            
            // pass 2: fill in the blanks
            for (key, frame) in keyMap {
                var keyView = self.generateKey()
                setupKey(keyView, key, frame)
            }
            
            self.updateKeyAppearanceTemp()
            
            CATransaction.commit()
        }
    }

    func updateKeyAppearanceTemp() {
