Skip to content

Commit

Permalink
Merge pull request #51 from sonsongithub/improve-liveview
Browse files Browse the repository at this point in the history
LiveView improvement
  • Loading branch information
sonsongithub authored Apr 24, 2017
2 parents 6e48249 + 62d6f62 commit 0e541c1
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 19 deletions.
21 changes: 13 additions & 8 deletions Playgrounds/NumswPlayground.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,33 @@ import CoreGraphics
#endif

public class NumswPlayground {

internal init() {
#if os(iOS)
viewController = RenderTableViewController()
viewController = RenderTableViewController(state: viewState)
#endif
}

#if os(iOS)
public let viewController: RenderTableViewController
internal let viewState = RenderTableViewController.State()
#endif

public func append(renderer: ChartRenderer) {
renderers.append(renderer)
public func append(renderer: Renderer) {
#if os(iOS)
viewController.append(renderer: renderer)
#endif
}

#if os(iOS)
public func print(_ matrix: Matrix<Double>) {
let matrixTextRenderer = MatrixTextRenderer(matrix)
renderers.append(matrixTextRenderer)
append(renderer: matrixTextRenderer)
}

public func print(_ string: String) {
let textRenderer = TextRenderer(string)
renderers.append(textRenderer)
append(renderer: textRenderer)
}
#endif

Expand Down Expand Up @@ -85,13 +89,14 @@ public class NumswPlayground {
return _shared!
}

private var renderers: [Renderer] = [] {
//private var renderers: [Renderer] = []
/*private var renderers: [ChartRenderer] = [] {
didSet {
#if os(iOS)
viewController.renderers = renderers.map { $0 as Renderer }
viewController.replace(renderers: renderers.map { $0 as Renderer })
#endif
}
}
}*/

private var chartBuilder: ChartBuilder?

Expand Down
68 changes: 68 additions & 0 deletions Playgrounds/PlaygroundKeyValueStore.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// PlaygroundKeyValueStore.swift
// sandbox
//
// Created by Yusuke Ito on 4/16/17.
// Copyright © 2017 sonson. All rights reserved.
//


// PlaygroundKeyValueStore as UserDefautls for sandbox app

#if os(iOS)
import Foundation


#if SANDBOX_APP
// sandbox app
internal enum PlaygroundValue {
case array([PlaygroundValue])
case boolean(Bool)
case data(Data)
case date(Date)
case dictionary([String: PlaygroundValue])
case floatingPoint(Double)
case integer(Int)
case string(String)
}
internal class PlaygroundKeyValueStore {
static let current = PlaygroundKeyValueStore()
private init() {

}
subscript(key: String) -> PlaygroundValue? {
set {
print("KeyValueStore setting \(newValue as Any) for \(key)")
if let value = newValue {
switch value {
case .floatingPoint(let double):
UserDefaults.standard.set(double, forKey: key)
case .integer(let integer):
UserDefaults.standard.set(integer, forKey: key)
default:
fatalError("Not implemented")
}
} else {
UserDefaults.standard.removeObject(forKey: key)
}
}
get {
print("KeyValueStore getting for \(key)")
guard let object = UserDefaults.standard.object(forKey: key) else {
return nil
}
if let integer = object as? Int {
return .integer(integer)
} else if let double = object as? Double {
return .floatingPoint(double)
} else {
fatalError("Not implemented")
}
}
}
}
#else
// playground
#endif

#endif
30 changes: 25 additions & 5 deletions Playgrounds/RenderTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
import UIKit

internal class RenderTableViewCell: UITableViewCell {

private static let renderQueue = DispatchQueue(label: "Renderer-Queue")

var renderer: Renderer? {
willSet {
Expand All @@ -22,7 +24,7 @@ internal class RenderTableViewCell: UITableViewCell {

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: .default, reuseIdentifier: reuseIdentifier)
print("RenderTableViewCell init")
//print("RenderTableViewCell init")
self.separatorInset = .zero
self.selectionStyle = .none
renderImageView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
Expand All @@ -36,14 +38,18 @@ internal class RenderTableViewCell: UITableViewCell {
}

deinit {
print("RenderTableViewCell deinit")
//print("RenderTableViewCell deinit")
}

private var renderedImageSize = CGSize.zero

private var isImageViewDirty: Bool {
return renderedImageSize != self.contentView.bounds.size
}

func updateImageViewIfNeeded() {
print("rendering bounds: \(self.contentView.bounds)")
if renderedImageSize == self.contentView.bounds.size &&
if isImageViewDirty == false &&
self.renderImageView.image != nil {
// already rendered
return
Expand All @@ -53,9 +59,23 @@ internal class RenderTableViewCell: UITableViewCell {

private func updateImageView() {
guard let renderer = self.renderer else { return }
let image = renderer.renderToImage(size: self.contentView.bounds.size)
self.renderImageView.image = image
let withFadeAnimation = isImageViewDirty && self.renderImageView.image != nil

renderedImageSize = self.contentView.bounds.size
type(of: self).renderQueue.async {
let image = renderer.renderToImage(size: self.contentView.bounds.size)
DispatchQueue.main.async {
self.renderImageView.image = image

if withFadeAnimation {
let transition = CATransition()
transition.duration = 0.25
transition.timingFunction = CAMediaTimingFunction(name: kCAMediaTimingFunctionEaseInEaseOut)
transition.type = kCATransitionFade
self.renderImageView.layer.add(transition, forKey: nil)
}
}
}
}
}
#endif
124 changes: 118 additions & 6 deletions Playgrounds/RenderTableViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,18 @@

#if os(iOS)
import UIKit
import QuartzCore

#if SANDBOX_APP
// sandbox app
#else
// playground
import PlaygroundSupport
#endif

public class RenderTableViewController: UITableViewController, UZTextViewDelegate {

var renderers: [Renderer] = [] {
private var renderers: [Renderer] = [] {
didSet {
for i in 0..<renderers.count {
renderers[i].parentViewSize = self.tableView.frame.size
Expand All @@ -22,7 +30,41 @@ public class RenderTableViewController: UITableViewController, UZTextViewDelegat
}
}

public init() {
internal class State {
private enum Key: String {
case tableViewScrollOffsetX = "table_view_scrolloffset_x"
case tableViewScrollOffsetY = "table_view_scrolloffset_y"
case rendererCount = "renderer_count"
}

var tableViewScrollOffset: CGPoint = .zero
init() {
let kvs = PlaygroundKeyValueStore.current
if let xValue = kvs[Key.tableViewScrollOffsetX.rawValue],
case .floatingPoint(let x) = xValue,
let yValue = kvs[Key.tableViewScrollOffsetY.rawValue],
case .floatingPoint(let y) = yValue {
tableViewScrollOffset = CGPoint(x: x, y: y)
} else {
tableViewScrollOffset = .zero
}
}

func reset() {
tableViewScrollOffset = .zero
}

func sync() {
let kvs = PlaygroundKeyValueStore.current
kvs[Key.tableViewScrollOffsetX.rawValue] = .floatingPoint(Double(tableViewScrollOffset.x))
kvs[Key.tableViewScrollOffsetY.rawValue] = .floatingPoint(Double(tableViewScrollOffset.y))
}
}

private var state: State

internal init(state: State) {
self.state = state
super.init(style: .plain)
}

Expand All @@ -41,20 +83,65 @@ public class RenderTableViewController: UITableViewController, UZTextViewDelegat
tableView.register(TextTableViewCell.self, forCellReuseIdentifier: "TextTableViewCell")
}

// not in use?
/*public func replace(renderers: [Renderer]) {
self.renderers = renderers
self.tableView.reloadData()
}*/

public func removeAllRenderers() {
self.renderers = []
self.tableView.reloadData()
}

public func append(renderer: Renderer) {
// partial update
self.renderers.append(renderer)
self.tableView.reloadData()
//self.tableView.insertRows(at: [IndexPath(row: renderers.count-1, section: 0)], with: .none)
tableView.reloadData()

// scroll to previous content position
let lastRowOffset = tableView.rectForRow(at: IndexPath(row: renderers.count-1, section: 0))

let viewBounds = UIEdgeInsetsInsetRect(view.bounds, tableView.contentInset)
let maxYOffset = max(lastRowOffset.origin.y + lastRowOffset.size.height - viewBounds.size.height, 0) // 0 ~
let offset = CGPoint(x: state.tableViewScrollOffset.x, y: min(maxYOffset, state.tableViewScrollOffset.y))
tableView.setContentOffset(offset, animated: false)
}

// some times not invoked?? on iPad playground
public override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
// will ??
//self.tableView.reloadData()
updateVisibleCellImagesIfNeeded()
}

// some times not invoked?? on iPad playground
public override func viewWillLayoutSubviews() {
super.viewWillLayoutSubviews()
tableView.visibleCells.flatMap({ $0 as? RenderTableViewCell}).forEach({ $0.updateImageViewIfNeeded() })
updateVisibleCellImagesIfNeeded()
}

private func updateVisibleCellImagesIfNeeded() {
// on orientation changed
for view in tableView.visibleCells {
(view as? RenderTableViewCell)?.updateImageViewIfNeeded()
}
}

// some times not invoked?? on iPad playground
/*public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
updateVisibleCellImagesIfNeeded()

super.viewWillTransition(to: size, with: coordinator)
}*/

// TODO:
// `view*LayoutSubviews` seems to be not invoked on iPad playground
// temporary, we use this deprecated method until the solution is found
public override func willAnimateRotation(to toInterfaceOrientation: UIInterfaceOrientation, duration: TimeInterval) {
super.willAnimateRotation(to: toInterfaceOrientation, duration: duration)
state.reset()
updateVisibleCellImagesIfNeeded()
}

public override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
Expand Down Expand Up @@ -98,5 +185,30 @@ public class RenderTableViewController: UITableViewController, UZTextViewDelegat
public func selectingStringEnded(_ textView: UZTextView) {
self.tableView.isScrollEnabled = true
}

#if SANDBOX_APP
// Tap any table view cell to dismiss
public override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
self.navigationController?.popViewController(animated: true)
}
#endif

public override func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
storeTableViewScrollOffset()
}

public override func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
storeTableViewScrollOffset()
}

public override func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
storeTableViewScrollOffset()
}

private func storeTableViewScrollOffset() {
// store tableview scroll offset
state.tableViewScrollOffset = tableView.contentOffset
state.sync()
}
}
#endif
6 changes: 6 additions & 0 deletions Playgrounds/sandbox/sandbox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
148967C11E9786D900F19D91 /* TextTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148967C01E9786D900F19D91 /* TextTableViewCell.swift */; };
148967C31E9786F400F19D91 /* RenderTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148967C21E9786F400F19D91 /* RenderTableViewCell.swift */; };
148967C71E97921800F19D91 /* SelectTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 148967C61E97921800F19D91 /* SelectTableViewController.swift */; };
52CCAD111EA3913500D6D6EB /* PlaygroundKeyValueStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52CCAD101EA3913500D6D6EB /* PlaygroundKeyValueStore.swift */; };
52CCAD121EA3913500D6D6EB /* PlaygroundKeyValueStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52CCAD101EA3913500D6D6EB /* PlaygroundKeyValueStore.swift */; };
D68EF7091E86C8C600927991 /* AxisRenderer.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A0B5BB1E6FE801004AE3B4 /* AxisRenderer.swift */; };
D68EF70A1E86C8C600927991 /* Chart.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A0B5BC1E6FE801004AE3B4 /* Chart.swift */; };
D68EF70B1E86C8C600927991 /* ChartBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = D6A0B5BD1E6FE801004AE3B4 /* ChartBuilder.swift */; };
Expand Down Expand Up @@ -128,6 +130,7 @@
148967C01E9786D900F19D91 /* TextTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = TextTableViewCell.swift; path = ../TextTableViewCell.swift; sourceTree = "<group>"; };
148967C21E9786F400F19D91 /* RenderTableViewCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = RenderTableViewCell.swift; path = ../RenderTableViewCell.swift; sourceTree = "<group>"; };
148967C61E97921800F19D91 /* SelectTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SelectTableViewController.swift; sourceTree = "<group>"; };
52CCAD101EA3913500D6D6EB /* PlaygroundKeyValueStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PlaygroundKeyValueStore.swift; path = ../PlaygroundKeyValueStore.swift; sourceTree = "<group>"; };
52E1EE3E1E6A8A2E008D19BB /* sandboxTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = sandboxTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
52E1EE421E6A8A2E008D19BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D68EF6FD1E86C89600927991 /* NumswRenderer.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = NumswRenderer.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -279,6 +282,7 @@
144A150B1E971A1400B42288 /* MatrixTextRenderer.swift */,
D6A0B5C41E6FE801004AE3B4 /* NumswPlayground_Playground.swift */,
D6A0B5C51E6FE801004AE3B4 /* NumswPlayground.swift */,
52CCAD101EA3913500D6D6EB /* PlaygroundKeyValueStore.swift */,
D6A0B5C61E6FE801004AE3B4 /* Renderer.swift */,
D6A0B5C91E6FE801004AE3B4 /* RendererUtil.swift */,
D6A0B5CA1E6FE801004AE3B4 /* RenderScrollViewController.swift */,
Expand Down Expand Up @@ -606,6 +610,7 @@
144A150C1E971A1400B42288 /* MatrixTextRenderer.swift in Sources */,
D68EF7101E86C8C600927991 /* LineGraph.swift in Sources */,
144A15101E971B9D00B42288 /* UZCursor.swift in Sources */,
52CCAD111EA3913500D6D6EB /* PlaygroundKeyValueStore.swift in Sources */,
D68EF7151E86C8C600927991 /* RendererUtil.swift in Sources */,
144A15131E971C0400B42288 /* UZLoupe.swift in Sources */,
144A15121E971B9D00B42288 /* UZTextView.swift in Sources */,
Expand All @@ -630,6 +635,7 @@
D68EF77B1E86DA4C00927991 /* ScatterGraphRenderer.swift in Sources */,
D68EF77C1E86DA4C00927991 /* ScatterGraph.swift in Sources */,
D68EF77D1E86DA4C00927991 /* Renderer.swift in Sources */,
52CCAD121EA3913500D6D6EB /* PlaygroundKeyValueStore.swift in Sources */,
D68EF77E1E86DA4C00927991 /* DummyData.swift in Sources */,
D68EF77F1E86DA4C00927991 /* RenderScrollViewController.swift in Sources */,
D68EF7801E86DA4C00927991 /* LineGraph.swift in Sources */,
Expand Down

0 comments on commit 0e541c1

Please sign in to comment.