【问题标题】:AxesDrawer cannot draw swift3 xcode 8AxesDrawer 无法绘制 swift3 xcode 8
【发布时间】:2016-12-16 20:10:40
【问题描述】:

我正在尝试制作一个图形计算器应用程序,但我无法让 AxesDrawer 工作。这是来自斯坦福大学的 swift 2 课程,我不知道如何使用 UIBezierPath 等来绘制它

AxesDrawer.swift:

    import UIKit

class AxesDrawer
{
    private struct Constants {
        static let HashmarkSize: CGFloat = 6
    }

    var color = UIColor.blue
    var minimumPointsPerHashmark: CGFloat = 40
    var contentScaleFactor: CGFloat = 1 // set this from UIView's contentScaleFactor to position axes with maximum accuracy

    convenience init(color: UIColor, contentScaleFactor: CGFloat) {
        self.init()
        self.color = color
        self.contentScaleFactor = contentScaleFactor
    }

    convenience init(color: UIColor) {
        self.init()
        self.color = color
    }

    convenience init(contentScaleFactor: CGFloat) {
        self.init()
        self.contentScaleFactor = contentScaleFactor
    }

    // this method is the heart of the AxesDrawer
    // it draws in the current graphic context's coordinate system
    // therefore origin and bounds must be in the current graphics context's coordinate system
    // pointsPerUnit is essentially the "scale" of the axes
    // e.g. if you wanted there to be 100 points along an axis between -1 and 1,
    //    you'd set pointsPerUnit to 50

    func drawAxesInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat)
    {
        UIGraphicsGetCurrentContext()!.saveGState()
        color.set()
        let path = UIBezierPath()
        path.move(to: CGPoint(x: bounds.minX, y: align(coordinate: origin.y)))
        path.addLine(to: CGPoint(x: bounds.maxX, y: align(coordinate: origin.y)))
        path.move(to: CGPoint(x: align(coordinate: origin.x), y: bounds.minY))
        path.addLine(to: CGPoint(x: align(coordinate: origin.x), y: bounds.maxY))
        path.stroke()
        drawHashmarksInRect(bounds: bounds, origin: origin, pointsPerUnit: abs(pointsPerUnit))
        UIGraphicsGetCurrentContext()!.restoreGState()
    }

    // the rest of this class is private

    private func drawHashmarksInRect(bounds: CGRect, origin: CGPoint, pointsPerUnit: CGFloat)
    {
        if ((origin.x >= bounds.minX) && (origin.x <= bounds.maxX)) || ((origin.y >= bounds.minY) && (origin.y <= bounds.maxY))
        {
            // figure out how many units each hashmark must represent
            // to respect both pointsPerUnit and minimumPointsPerHashmark
            var unitsPerHashmark = minimumPointsPerHashmark / pointsPerUnit
            if unitsPerHashmark < 1 {
                unitsPerHashmark = pow(10, ceil(log10(unitsPerHashmark)))
            } else {
                unitsPerHashmark = floor(unitsPerHashmark)
            }

            let pointsPerHashmark = pointsPerUnit * unitsPerHashmark

            // figure out which is the closest set of hashmarks (radiating out from the origin) that are in bounds
            var startingHashmarkRadius: CGFloat = 1
            if !bounds.contains(origin) {
                let leftx = max(origin.x - bounds.maxX, 0)
                let rightx = max(bounds.minX - origin.x, 0)
                let downy = max(origin.y - bounds.minY, 0)
                let upy = max(bounds.maxY - origin.y, 0)
                startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy)) / pointsPerHashmark + 1
            }

            // now create a bounding box inside whose edges those four hashmarks lie
            let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2
            var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize))

            // formatter for the hashmark labels
            let formatter = NumberFormatter()
            formatter.maximumFractionDigits = Int(-log10(Double(unitsPerHashmark)))
            formatter.minimumIntegerDigits = 1

            // radiate the bbox out until the hashmarks are further out than the bounds
            while !bbox.contains(bounds)
            {

                let label = formatter.string(from: NSNumber(value: Int(origin.x-bbox.minX / pointsPerUnit)))
                if let leftHashmarkPoint = alignedPoint(x: bbox.minX, y: origin.y, insideBounds:bounds) {
                    drawHashmarkAtLocation(location: leftHashmarkPoint, .Top("-\(label)"))
                }
                if let rightHashmarkPoint = alignedPoint(x: bbox.maxX, y: origin.y, insideBounds:bounds) {
                    drawHashmarkAtLocation(location: rightHashmarkPoint, AnchoredText.Top(label!))
                }
                if let topHashmarkPoint = alignedPoint(x: origin.x, y: bbox.minY, insideBounds:bounds) {
                    drawHashmarkAtLocation(location: topHashmarkPoint, AnchoredText.Left(label!))
                }
                if let bottomHashmarkPoint = alignedPoint(x: origin.x, y: bbox.maxY, insideBounds:bounds) {
                    drawHashmarkAtLocation(location: bottomHashmarkPoint, .Left("-\(label)"))
                }
                bbox.insetBy(dx: -pointsPerHashmark, dy: -pointsPerHashmark)
            }
        }
    }

    private func drawHashmarkAtLocation(location: CGPoint, _ text: AnchoredText)
    {
        var dx: CGFloat = 0, dy: CGFloat = 0
        switch text {
            case .Left: dx = Constants.HashmarkSize / 2
            case .Right: dx = Constants.HashmarkSize / 2
            case .Top: dy = Constants.HashmarkSize / 2
            case .Bottom: dy = Constants.HashmarkSize / 2
        }

        let path = UIBezierPath()
        path.move(to: CGPoint(x: location.x-dx, y: location.y-dy))
        path.addLine(to: CGPoint(x: location.x+dx, y: location.y+dy))
        path.stroke()

        text.drawAnchoredToPoint(location: location, color: color)
    }

    private enum AnchoredText
    {
        case Left(String)
        case Right(String)
        case Top(String)
        case Bottom(String)

        static let VerticalOffset: CGFloat = 3
        static let HorizontalOffset: CGFloat = 6

        func drawAnchoredToPoint(location: CGPoint, color: UIColor) {
            let attributes = [
                NSFontAttributeName : UIFont.preferredFont(forTextStyle: UIFontTextStyle.footnote),
                NSForegroundColorAttributeName : color
            ]
            var textRect = CGRect(center: location, size: text.size(attributes: attributes))
            switch self {
                case .Top: textRect.origin.y += textRect.size.height / 2 + AnchoredText.VerticalOffset
                case .Left: textRect.origin.x += textRect.size.width / 2 + AnchoredText.HorizontalOffset
                case .Bottom: textRect.origin.y -= textRect.size.height / 2 + AnchoredText.VerticalOffset
                case .Right: textRect.origin.x -= textRect.size.width / 2 + AnchoredText.HorizontalOffset
            }
            text.draw(in: textRect, withAttributes: attributes)
        }

        var text: String {
            switch self {
                case .Left(let text): return text
                case .Right(let text): return text
                case .Top(let text): return text
                case .Bottom(let text): return text
            }
        }
    }

    // we want the axes and hashmarks to be exactly on pixel boundaries so they look sharp
    // setting contentScaleFactor properly will enable us to put things on the closest pixel boundary
    // if contentScaleFactor is left to its default (1), then things will be on the nearest "point" boundary instead
    // the lines will still be sharp in that case, but might be a pixel (or more theoretically) off of where they should be

    private func alignedPoint(x x: CGFloat, y: CGFloat, insideBounds: CGRect? = nil) -> CGPoint?
    {
        let point = CGPoint(x: align(coordinate: x), y: align(coordinate: y))
        if let permissibleBounds = insideBounds, !permissibleBounds.contains(point) {
            return nil
        }
        return point
    }

    private func align(coordinate: CGFloat) -> CGFloat {
        return round(coordinate * contentScaleFactor) / contentScaleFactor
    }
}

extension CGRect
{
    init(center: CGPoint, size: CGSize) {
        self.init(x: center.x-size.width/2, y: center.y-size.height/2, width: size.width, height: size.height)
    }
}

ViewController.swift:

    import UIKit
var calculatorCount = 0
class CalculatorViewController: UIViewController {
    var graphl = GraphView()
    private var on = true
    @IBOutlet private var display: UILabel!

    private var userIsInTheMiddleOfTyping = false

    override func viewDidLoad() {
        super.viewDidLoad()
        calculatorCount +=  1
        //print("Loaded up a new Calculator (count = \(calculatorCount))")
        brain.addUnaryOperation(symbol: "Z") { [ weak weakSelf = self ] in
            weakSelf?.display.textColor = UIColor.red
            return sqrt($0)
        }
        graphl.print2()
    }


    deinit {
        calculatorCount -= 1
        //print(" Calculator left the heap (count = \(calculatorCount))")
    }


    @IBAction func off(_ sender: UIButton) {
        on = false
    }

    @IBAction func on(_ sender: UIButton) {
        on = true
    }

    @IBAction private func tocuhDigit(_ sender: UIButton) {
        if on {
            let digit = sender.currentTitle!
            if userIsInTheMiddleOfTyping {
                let textCurrentlyInDisplay = display.text!
                display.text =  textCurrentlyInDisplay + digit
            } else {
                display.text = digit
            }
            userIsInTheMiddleOfTyping = true
        }
    }
    private var displayValue: Double {
        get {
            return Double(display.text!)!
        }
        set {
            display.text = String(newValue)

        }
    }
    var savedProgram: CalculatorBrain.PropertyList?
    @IBAction func save() {
        savedProgram = brain.program

    }

    @IBAction func restore() {
        if savedProgram != nil {
            brain.program = savedProgram!
            displayValue = brain.result
        }
    }

    private var brain = CalculatorBrain()

    @IBAction func Reset(_ sender: UIButton) {
        if on {

            displayValue = 0
        }
    }
    @IBAction private func performOperation(_ sender: UIButton) {
        if userIsInTheMiddleOfTyping && on {
            brain.setOperand(operand: displayValue)
            userIsInTheMiddleOfTyping = false
        }

        if let mathematicalSymbol = sender.currentTitle {
            brain.perofrmOperation(symbol: mathematicalSymbol)

        }
        displayValue = brain.result
    }

}

CalculatorBrain.swift:

    import Foundation


class CalculatorBrain {

    private var accumulator = 0.0
    private var internalProgram = [AnyObject]()

    func setOperand(operand: Double) {
        accumulator = operand
        internalProgram.append(operand as AnyObject)

    }
    func addUnaryOperation(symbol: String, operation: @escaping (Double) -> Double) {
        operations[symbol] = Operation.UnaryOperation(operation)
    }

    private var operations: Dictionary<String, Operation> = [
        "π" :Operation.Constant(M_PI),
        "e" : Operation.Constant(M_E),
        "±" : Operation.UnaryOperation({ -$0 }),
        "∓" : Operation.UnaryOperation({+$0}),
        "√" : Operation.UnaryOperation(sqrt),  //sqrt,
        "cos" : Operation.UnaryOperation(cos),
        "×" : Operation.BinaryOperation({ $0 * $1 }),
        "-" : Operation.BinaryOperation({ $0 - $1 }),
        "+" : Operation.BinaryOperation({ $0 + $1 }),
        "÷" : Operation.BinaryOperation({ $0 / $1 }),
        "=" : Operation.Equals,
        "i" : Operation.Constant(sqrt(-1)),
        "x2" : Operation.UnaryOperation({$0 * $0}),
        "xb" : Operation.BinaryOperation2({pow($0, $1)})
    ]

    private enum Operation {
        case Constant(Double)
        case UnaryOperation((Double) -> Double)
        case BinaryOperation((Double, Double) -> Double)
        case Equals
        case BinaryOperation2((Double, Double) -> Double)

    }

    func perofrmOperation(symbol: String) {
        internalProgram.append(symbol as AnyObject)
        if let operation = operations[symbol] {
            switch operation {
            case .Constant(let value): accumulator = value
            case .UnaryOperation(let function): accumulator = function(accumulator)
            case .BinaryOperation(let function):  executePendingBinaryOperation()
            pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)
            case .Equals:
                executePendingBinaryOperation()

            case .BinaryOperation2(let function):
                pending = PendingBinaryOperationInfo(binaryFunction: function, firstOperand: accumulator)




            }
        }


    }
    private func executePendingBinaryOperation() {
        if pending != nil {
            accumulator = pending!.binaryFunction(pending!.firstOperand, accumulator)
        }
    }

    private var pending: PendingBinaryOperationInfo?

    private struct PendingBinaryOperationInfo {
        var binaryFunction: (Double, Double) -> Double
        var firstOperand: Double

    }

    typealias PropertyList = AnyObject
    var program: PropertyList {
        get {
            return internalProgram as CalculatorBrain.PropertyList
        }
        set {
            clear()
            if let arrayOfOps = newValue as? [AnyObject] {
                for op in arrayOfOps {
                    if let operand = op as? Double {
                        setOperand(operand: operand)
                    } else if let operation = op as? String {
                        perofrmOperation(symbol: operation)
                    }
                }
            }
        }
    }
    func clear() {
        accumulator = 0.0
        pending = nil
        internalProgram.removeAll()
    }

    var result: Double {
        get {
            return accumulator

        }
    }

}

AppDelegate.swift:

    import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?


    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        return true
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }


}

Main.storyboard:
[buttons are in stack a stack view and created a UIView for the graph][1]


  [1]: https://i.stack.imgur.com/oxwmw.png

【问题讨论】:

    标签: swift3 drawing xcode8 uibezierpath


    【解决方案1】:

    我知道这个问题已经有很多时间了,但是我今天提出了同样的问题,我希望这个答案可以帮助其他人:

    您可以按照以下步骤操作:

    1. 在 Xcode 上创建一个新项目。
    2. 将“AxesDrawer.swift”文件添加到您的项目中(文件 -> 将文件添加到“ProjectName”...
    3. 在项目的主文件夹中使用 UIView 子类创建一个新的 Cocoa Touch Class 文件。
    4. Main.storyboard 上添加一个视图,并将该视图的类名设置为与上面创建的文件相同
    5. 使用以下代码覆盖第 3 步创建的文件中的绘图函数:

      覆盖 func draw(_ rect: CGRect) {

      //Draw axes
      let axes: AxesDrawer = AxesDrawer.init(color: UIColor.black, contentScaleFactor: CGFloat(1))
      
      axes.drawAxes(in: CGRect(origin: CGPoint(x: bounds.midX, y: bounds.midY),
                               size: CGSize(width: 1000, height: -1000)),
                    origin: CGPoint(x: bounds.midX, y: bounds.midY),
                    pointsPerUnit: CGFloat(2))
      
      axes.drawAxes(in: CGRect(origin: CGPoint(x: bounds.midX, y: bounds.midY),
                               size: CGSize(width: -1000, height: 1000)),
                    origin: CGPoint(x: bounds.midX, y: bounds.midY),
                    pointsPerUnit: CGFloat(2))
      //End Draw axes
      

      }

    【讨论】:

      【解决方案2】:
      //
      //  AxesDrawer.swift
      //  Calculator
      //
      //  Created by CS193p Instructor.
      //  Copyright © 2015-17 Stanford University.
      //  All rights reserved.
      //
      
      import UIKit
      
      struct AxesDrawer
      {
          var color: UIColor
          var contentScaleFactor: CGFloat             // set this from UIView's contentScaleFactor to position axes with maximum accuracy
          var minimumPointsPerHashmark: CGFloat = 40  // public even though init doesn't accommodate setting it (it's rare to want to change it)
      
          init(color: UIColor = UIColor.blue, contentScaleFactor: CGFloat = 1) {
              self.color = color
              self.contentScaleFactor = contentScaleFactor
          }
      
          // this method is the heart of the AxesDrawer
          // it draws in the current graphic context's coordinate system
          // therefore origin and bounds must be in the current graphics context's coordinate system
          // pointsPerUnit is essentially the "scale" of the axes
          // e.g. if you wanted there to be 100 points along an axis between -1 and 1,
          //    you'd set pointsPerUnit to 50
      
          func drawAxes(in rect: CGRect, origin: CGPoint, pointsPerUnit: CGFloat)
          {
              UIGraphicsGetCurrentContext()?.saveGState()
              color.set()
              let path = UIBezierPath()
              path.move(to: CGPoint(x: rect.minX, y: origin.y).aligned(usingScaleFactor: contentScaleFactor)!)
              path.addLine(to: CGPoint(x: rect.maxX, y: origin.y).aligned(usingScaleFactor: contentScaleFactor)!)
              path.move(to: CGPoint(x: origin.x, y: rect.minY).aligned(usingScaleFactor: contentScaleFactor)!)
              path.addLine(to: CGPoint(x: origin.x, y: rect.maxY).aligned(usingScaleFactor: contentScaleFactor)!)
              path.stroke()
              drawHashmarks(in: rect, origin: origin, pointsPerUnit: abs(pointsPerUnit))
              UIGraphicsGetCurrentContext()?.restoreGState()
          }
      
          // the rest of this class is private
      
          private struct Constants {
              static let hashmarkSize: CGFloat = 6
          }
      
          private let formatter = NumberFormatter() // formatter for the hashmark labels
      
          private func drawHashmarks(in rect: CGRect, origin: CGPoint, pointsPerUnit: CGFloat)
          {
              if ((origin.x >= rect.minX) && (origin.x <= rect.maxX)) || ((origin.y >= rect.minY) && (origin.y <= rect.maxY))
              {
                  // figure out how many units each hashmark must represent
                  // to respect both pointsPerUnit and minimumPointsPerHashmark
                  var unitsPerHashmark = minimumPointsPerHashmark / pointsPerUnit
                  if unitsPerHashmark < 1 {
                      unitsPerHashmark = pow(10, ceil(log10(unitsPerHashmark)))
                  } else {
                      unitsPerHashmark = floor(unitsPerHashmark)
                  }
      
                  let pointsPerHashmark = pointsPerUnit * unitsPerHashmark
      
                  // figure out which is the closest set of hashmarks (radiating out from the origin) that are in rect
                  var startingHashmarkRadius: CGFloat = 1
                  if !rect.contains(origin) {
                      let leftx = max(origin.x - rect.maxX, 0)
                      let rightx = max(rect.minX - origin.x, 0)
                      let downy = max(origin.y - rect.minY, 0)
                      let upy = max(rect.maxY - origin.y, 0)
                      startingHashmarkRadius = min(min(leftx, rightx), min(downy, upy)) / pointsPerHashmark + 1
                  }
      
                  // pick a reasonable number of fraction digits
                  formatter.maximumFractionDigits = Int(-log10(Double(unitsPerHashmark)))
                  formatter.minimumIntegerDigits = 1
      
                  // now create a bounding box inside whose edges those four hashmarks lie
                  let bboxSize = pointsPerHashmark * startingHashmarkRadius * 2
                  var bbox = CGRect(center: origin, size: CGSize(width: bboxSize, height: bboxSize))
      
                  // radiate the bbox out until the hashmarks are further out than the rect
                  while !bbox.contains(rect)
                  {
                      let label = formatter.string(from: (origin.x-bbox.minX)/pointsPerUnit)!
                      if let leftHashmarkPoint = CGPoint(x: bbox.minX, y: origin.y).aligned(inside: rect, usingScaleFactor: contentScaleFactor) {
                          drawHashmark(at: leftHashmarkPoint, label: .top("-\(label)"))
                      }
                      if let rightHashmarkPoint = CGPoint(x: bbox.maxX, y: origin.y).aligned(inside: rect, usingScaleFactor: contentScaleFactor) {
                          drawHashmark(at: rightHashmarkPoint, label: .top(label))
                      }
                      if let topHashmarkPoint = CGPoint(x: origin.x, y: bbox.minY).aligned(inside: rect, usingScaleFactor: contentScaleFactor) {
                          drawHashmark(at: topHashmarkPoint, label: .left(label))
                      }
                      if let bottomHashmarkPoint = CGPoint(x: origin.x, y: bbox.maxY).aligned(inside: rect, usingScaleFactor: contentScaleFactor) {
                          drawHashmark(at: bottomHashmarkPoint, label: .left("-\(label)"))
                      }
                      bbox = bbox.insetBy(dx: -pointsPerHashmark, dy: -pointsPerHashmark)
                  }
              }
          }
      
          private func drawHashmark(at location: CGPoint, label: AnchoredText)
          {
              var dx: CGFloat = 0, dy: CGFloat = 0
              switch label {
                  case .left: dx = Constants.hashmarkSize / 2
                  case .right: dx = Constants.hashmarkSize / 2
                  case .top: dy = Constants.hashmarkSize / 2
                  case .bottom: dy = Constants.hashmarkSize / 2
              }
      
              let path = UIBezierPath()
              path.move(to: CGPoint(x: location.x-dx, y: location.y-dy))
              path.addLine(to: CGPoint(x: location.x+dx, y: location.y+dy))
              path.stroke()
      
              label.draw(at: location, usingColor: color)
          }
      
          private enum AnchoredText
          {
              case left(String)
              case right(String)
              case top(String)
              case bottom(String)
      
              static let verticalOffset: CGFloat = 3
              static let horizontalOffset: CGFloat = 6
      
              func draw(at location: CGPoint, usingColor color: UIColor) {
                  let attributes = [
                      NSFontAttributeName : UIFont.preferredFont(forTextStyle: .footnote),
                      NSForegroundColorAttributeName : color
                  ]
                  var textRect = CGRect(center: location, size: text.size(attributes: attributes))
                  switch self {
                      case .top: textRect.origin.y += textRect.size.height / 2 + AnchoredText.verticalOffset
                      case .left: textRect.origin.x += textRect.size.width / 2 + AnchoredText.horizontalOffset
                      case .bottom: textRect.origin.y -= textRect.size.height / 2 + AnchoredText.verticalOffset
                      case .right: textRect.origin.x -= textRect.size.width / 2 + AnchoredText.horizontalOffset
                  }
                  text.draw(in: textRect, withAttributes: attributes)
              }
      
              var text: String {
                  switch self {
                      case .left(let text): return text
                      case .right(let text): return text
                      case .top(let text): return text
                      case .bottom(let text): return text
                  }
              }
          }
      }
      
      private extension CGPoint
      {
          func aligned(inside bounds: CGRect? = nil, usingScaleFactor scaleFactor: CGFloat = 1.0) -> CGPoint?
          {
              func align(_ coordinate: CGFloat) -> CGFloat {
                  return round(coordinate * scaleFactor) / scaleFactor
              }
              let point = CGPoint(x: align(x), y: align(y))
              if let permissibleBounds = bounds, !permissibleBounds.contains(point) {
                  return nil
              }
              return point
          }
      }
      
      private extension NumberFormatter
      {
          func string(from point: CGFloat) -> String? {
              return string(from: NSNumber(value: Double(point)))
          }
      }
      
      private extension CGRect
      {
          init(center: CGPoint, size: CGSize) {
              self.init(x: center.x-size.width/2, y: center.y-size.height/2, width: size.width, height: size.height)
          }
      }
      

      【讨论】:

      • 当你回答一个问题时,解释你做了什么,而不是把所有的代码放在一起。参考How to answer
      猜你喜欢
      • 2017-01-26
      • 1970-01-01
      • 1970-01-01
      • 2017-07-08
      • 1970-01-01
      • 1970-01-01
      • 2017-01-31
      • 2016-12-29
      • 2017-02-27
      相关资源
      最近更新 更多