 
     grid.insert(cells, at: index)
 
-    updateGrid() // TODO: Is this the correct place?
-
+    updateGrid()
+    
     return row
   }
 
-  public func row(at index: Int) -> GridRow {
-    return rows[index]
-  }
-
-  public func removeColumn(at index: Int) {
+  //open func moveRow(at fromIndex: Int, to toIndex: Int) { }
+
+  open func removeRow(at index: Int) {
+    rows.remove(at: index)
+    grid.remove(at: index)
+  }
+
+  @discardableResult
+  open func addColumn(with views: [UIView]) -> GridColumn {
+    return insertColumn(at: columns.count, with: views)
+  }
+
+  open func insertColumn(at index: Int, with views: [UIView]) -> GridColumn {
+    for _ in 0..<max(0, views.count - numberOfRows) {
+      addRow(with: [])
+    }
+
+    let column = GridColumn(gridView: self)
+    columns.insert(column, at: index)
+
+    for (rowIndex, row) in rows.enumerated() {
+      let cell = GridCell(gridView: self)
+      cell.contentView = views[safe:rowIndex]
+      cell.row = row
+      cell.column = column
+
+      grid[rowIndex].insert(cell, at: index)
+    }
+
+    updateGrid()
+
+    return column
+  }
+
+  //open func moveColumn(at fromIndex: Int, to toIndex: Int) { }
+
+  open func removeColumn(at index: Int) {
     columns.remove(at: index)
     for (rowIndex, _) in grid.enumerated() {
       grid[rowIndex].remove(at: index)
     }
-
-    // TODO: updateGrid() ?
-  }
-
-  public func removeRow(at index: Int) {
-    rows.remove(at: index)
-    grid.remove(at: index)
-
-    // TODO: updateGrid() ?
-  }
+  }
+
+  // MARK: Position
+
+  open var xPlacement: GridViewXPlacement = .leading { didSet { updateGrid() } }
+
+  open var yPlacement: GridViewYPlacement = .bottom { didSet { updateGrid() } }
+
+  //open var rowAlignment: GridRowAlignment
+
+  // MARK: Space
+
+  open var columnSpacing: CGFloat = 0 { didSet { updateGrid() } }
+
+  open var rowSpacing: CGFloat = 0 { didSet { updateGrid() } }
+
+  // MARK: Merge
+
+  //open func mergeCells(inHorizontalRange hRange: NSRange, verticalRange vRange: NSRange)
+
 }
 
 // MARK: - GridRow

// TODO: fix runtime ambiguous position and size
// TODO: make UIView.animate possible, by not using updateGrid()

public class GridView: UIView {

  public var columnSpacing: CGFloat = 0 { didSet { updateGrid() } }
  public var numberOfColumns: Int { return columns.count }
  public var numberOfRows: Int { return rows.count }
  public var rowSpacing: CGFloat = 0 { didSet { updateGrid() } }
  public var xPlacement: GridViewXPlacement = .leading { didSet { updateGrid() } }
  public var yPlacement: GridViewYPlacement = .bottom { didSet { updateGrid() } }

  var columns: [GridColumn] = []
  var rows: [GridRow] = []
  var grid: [[GridCell]] = []

  public convenience init(views rows: [[UIView]]) {
    self.init()


    }
  }

  // MARK: Count

  open var numberOfColumns: Int {
    return columns.count
  }

  open var numberOfRows: Int {
    return rows.count
  }

  // MARK: Get

  open func row(at index: Int) -> GridRow {
    return rows[index]
  }

  open func index(of row: GridRow) -> Int? { // Note: NSGridView returns a non-optional Int
    return rows.index { $0 === row }
  }

  open func column(at index: Int) -> GridColumn {
    return columns[index]
  }

  open func index(of column: GridColumn) -> Int? { // Note: NSGridView returns a non-optional Int
    return columns.index { $0 === column }
  }

  open func cell(atColumnIndex columnIndex: Int, rowIndex: Int) -> GridCell {
    return grid[rowIndex][columnIndex]
  }

  open func cell(for view: UIView) -> GridCell? {
    for row in grid {
      for cell in row {
        if cell.contentView == view {
