exyte / Macaw

Powerful and easy-to-use vector graphics Swift library with SVG support
MIT License
6.01k stars 554 forks source link

Initialise MacawView Subcalss in a ViewController #666

Closed zyrondias closed 4 years ago

zyrondias commented 4 years ago

Hi Everyone, I am current new Swift and IOS development and I wanted to incorporate a graph that would change dynamically based on the data retrieved from Firebase. From a tutorial online I made my MacawView subclass but I can't seem to find a way to change the graph data once it been created I also made a similar class with no static methods but can't seem to initialise it since it needs an NSCoder decoder which I have no clue about : `import UIKit import Macaw

class MacawChartView: MacawView {

 static var dataSet : [GraphData] = [] {
    didSet {
        adjustData()
    }
}
static let maxValue = 100 // max value of the data in the graph
static let maxValueLineHeight = 300 // based on the view
static let lineWidth : Double = 500 // based on the view

static let dataDivisor = Double(maxValue)/Double(maxValueLineHeight)
static var adjustedData: [Double] = []
static var animations : [Animation] = []

required init?(coder aDecoder: NSCoder) {
    super.init(node: MacawChartView.createChart(), coder: aDecoder)
    backgroundColor = .clear
}

private static func adjustData(){
    adjustedData = dataSet.map({ $0.Score / dataDivisor })
}

private func reinit(){
    super.node = MacawChartView.createChart()
}

private static func createChart() -> Group {
    var items = addYaxisItems() + addXaxisItems()
    items.append(createBars())
    return Group(contents: items, place: .identity)
}

private static func addYaxisItems() -> [Node] {
    let maxlines = 10
    let lineInterval = Int(maxValue/maxlines)
    let YaxisHeight : Double = 300
    let lineSpacing : Double = 30

    var newNodes : [Node] = []

    for i in 1...maxlines{
        let y = YaxisHeight - (Double(i) * lineSpacing)

        let valueLine = Line(x1: -5, y1: y, x2: lineWidth, y2: y).stroke(fill: Color.black.with(a: 0.10))
        let valueText = Text(text: "\(i * lineInterval)", align: .max, baseline: .mid, place: .move(dx: -10, dy: y))
        valueText.fill = Color.black

        newNodes.append(valueLine)
        newNodes.append(valueText)
    }

    let yAxis = Line(x1: 0, y1: 0, x2: 0, y2: YaxisHeight).stroke(fill: Color.black.with(a: 0.25))
    newNodes.append(yAxis)

    return newNodes
}

private static func addXaxisItems() -> [Node] {
    let chartBaseY : Double = 300
    var newNodes : [Node] = []

    if(adjustedData.count > 0){
        for i in 1...adjustedData.count {
            let x = (Double(i) * 50 )
            let valueText = Text(text: dataSet[i - 1].Concept, align: .max, baseline: .mid,place: .move(dx: x, dy: chartBaseY + 15))

            valueText.fill = Color.black
            newNodes.append(valueText)
        }
    }

    let xAxis = Line(x1: 0, y1: chartBaseY, x2: lineWidth, y2: chartBaseY).stroke(fill: Color.black.with(a: 0.25))
    newNodes.append(xAxis)
    return newNodes
}

private static func createBars() -> Group {
    let fill =  LinearGradient(degree: 90, from: Color.init(val: 0xff4704), to: Color(val: 0xff4704).with(a: 0.33))
    let items = adjustedData.map { _ in Group() }

    animations = items.enumerated().map {(i : Int, item : Group ) in
        item.contentsVar.animation(delay: Double(i) * 0.1) {t in
            let height = adjustedData[i] * t
            let rect = Rect(x: Double(i) * 50 + 25, y: 300 - height , w: 30, h: height)
            return [rect.fill(with: fill)]
        }
    }
    return items.group()
}

static func playAnimations(){
    animations.combine().play()
}

private static func data() -> [GraphData] {

    return []
}

}`

I have a View Controller with a UIView where I would like to display the class but I can'tthe change the data since most of the methods and variables are static

ViewController: `import UIKit import Firebase

class TestPageController: UIViewController {

@IBOutlet weak var TopicName: UILabel!
@IBOutlet weak var topicDescription: UILabel!
@IBOutlet weak var TopicScore: UILabel!
@IBOutlet weak var TaketestButton: UIButton!
@IBOutlet weak var graphView: UIView!

private var topicName = ""
private let db = Firestore.firestore()
private var reference : DocumentReference?
 var selectedTopic : DocumentReference? {
    didSet{
        getTopicInfo()
    }
}

var data : [GraphData] = []

override func viewDidLoad() {
    super.viewDidLoad()
    setup()
    graphView.contentMode = .scaleAspectFit
    MacawChartView.playAnimations()
}

func getTopicInfo(){
    selectedTopic?.addSnapshotListener({ (document, error) in
        if(error != nil){
            print(error!.localizedDescription)
        }else{
            self.TopicScore.text = "Score: \(document?.get("Score") ?? "")"
            self.reference = document!.get("Topic") as? DocumentReference
            self.reference!.getDocument(completion: { (document, error) in
                if let document = document, document.exists {
                    self.topicName =  document.get("Topic Name") as! String
                    let description = document.get("Topic Description") as! String
                    self.TopicName.text = self.topicName
                    self.topicDescription.text = description
                    // Change graph data here 
                }
            })
        }
    })
}

@IBAction func TakeTestButtonPressed(_ sender: Any) {

}

func setup(){
    Utilities.styleFilledButton(TaketestButton)
}

func getGraphData()->[GraphData] {
    var data : [GraphData] = []
    var conceptGraph : [String : [Int]] = [:]
    selectedTopic?.collection("Questions").getDocuments(completion: { (query, error) in
        if(error != nil){
             print(error!.localizedDescription)
        }else{
            for document in query!.documents{
                let concept = document.get("Concept") as! String
                print(concept)
                let answer = document.get("IsCorrect") as! Int
                if(conceptGraph[concept] != nil){
                    conceptGraph[concept]![0] =  conceptGraph[concept]![0] + answer
                    conceptGraph[concept]![1] = conceptGraph[concept]![0] + 1
                }else{
                    conceptGraph[concept] = [answer,1]
                }
            }

            for (concept, score) in conceptGraph {
                var percentage = 0.0
                if(score[0] < 0){
                    percentage = 0
                }else{
                    percentage = (Double(score[0]) / Double(score[1])) * 100
                }

                data.append(GraphData(Concept: concept, Score: percentage))
            }
        }
    })

    return data
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    let destinationVC = segue.destination as! TestViewController
    destinationVC.TopicRef = reference
}

}`

amarunko commented 4 years ago

Hi, @zyrondias this is an example class without static content:

class MacawTextView: MacawView {

    required init?(coder aDecoder: NSCoder) {
        let text = Text(text: "Hello, World!", place: .move(dx: 145, dy: 100))
        super.init(frame: CGRect.zero)
        node = text
    }

    init() {
        let rect  = CGRect(x: 100, y: 300, width: 100, height: 100)
        let text = Text(text: "Hello, World!", fill: Color.red)
        super.init(frame: rect)
        node = text
    }
}

So, for changing any data you can just set node property of your class

ystrot commented 4 years ago

Closing this issue. Please reopen it if you have more questions.