【问题标题】:Why doesn't UIView.animateWithDuration affect this custom view?为什么 UIView.animateWithDuration 不影响这个自定义视图?
【发布时间】:2016-07-29 09:16:30
【问题描述】:

我设计了一个自定义标题视图,它可以遮盖图像并在底部边缘绘制边框,这是一个弧形。它看起来像这样:

这是该类的代码:

class HeaderView: UIView
{
    private let imageView = UIImageView()
    private let dimmerView = UIView()

    private let arcShape = CAShapeLayer()
    private let maskShape = CAShapeLayer() // Masks the image and the dimmer

    private let titleLabel = UILabel()

    @IBInspectable var image: UIImage? { didSet { self.imageView.image = self.image } }
    @IBInspectable var title: String? { didSet {self.titleLabel.text = self.title} }

    @IBInspectable var arcHeight: CGFloat? { didSet {self.setupLayers()} }

    // MARK: Initialization

    override init(frame: CGRect)
    {
        super.init(frame:frame)
        initMyStuff()
    }

    required init?(coder aDecoder: NSCoder)
    {
        super.init(coder:aDecoder)
        initMyStuff()
    }

    override func prepareForInterfaceBuilder()
    {
        backgroundColor = UIColor.clear()
    }

    internal func initMyStuff()
    {
        backgroundColor = UIColor.clear()

        titleLabel.font = Font.AvenirNext_Bold(24)
        titleLabel.text = "TITLE"
        titleLabel.textColor = UIColor.white()
        titleLabel.layer.shadowColor = UIColor.black().cgColor
        titleLabel.layer.shadowOffset = CGSize(width: 0.0, height: 2.0)

        titleLabel.layer.shadowRadius = 0.0;
        titleLabel.layer.shadowOpacity = 1.0;

        titleLabel.layer.masksToBounds = false
        titleLabel.layer.shouldRasterize = true

        imageView.contentMode = UIViewContentMode.scaleAspectFill
        addSubview(imageView)

        dimmerView.frame = self.bounds
        dimmerView.backgroundColor = UIColor(red: 0, green: 0, blue: 0, alpha: 0.6)
        addSubview(dimmerView)
        addSubview(titleLabel)

        // Add the shapes
        self.layer.addSublayer(arcShape)
        self.layer.addSublayer(maskShape)
        self.layer.masksToBounds = true // This seems to be unneeded...test more

        // Set constraints
        imageView.translatesAutoresizingMaskIntoConstraints = false
        imageView .autoPinEdgesToSuperviewEdges()
        titleLabel.autoCenterInSuperview()
    }

    func setupLayers()
    {
        let aHeight = arcHeight ?? 10

        // Create the arc shape
        arcShape.path = AppocalypseUI.createHorizontalArcPath(CGPoint(x: 0, y: bounds.size.height), width: bounds.size.width, arcHeight: aHeight)
        arcShape.strokeColor = UIColor.white().cgColor
        arcShape.lineWidth = 1.0
        arcShape.fillColor = UIColor.clear().cgColor

        // Create the mask shape
        let maskPath = AppocalypseUI.createHorizontalArcPath(CGPoint(x: 0, y: bounds.size.height), width: bounds.size.width, arcHeight: aHeight, closed: true)
        maskPath.moveTo(nil, x: bounds.size.width, y: bounds.size.height)
        maskPath.addLineTo(nil, x: bounds.size.width, y: 0)
        maskPath.addLineTo(nil, x: 0, y: 0)
        maskPath.addLineTo(nil, x: 0, y: bounds.size.height)

        //let current = CGPathGetCurrentPoint(maskPath);
        //print(current)

        let mask_Dimmer = CAShapeLayer()
        mask_Dimmer.path = maskPath.copy()

        maskShape.fillColor = UIColor(red: 0, green: 0, blue: 0, alpha: 1.0).cgColor
        maskShape.path = maskPath

        // Apply the masks
        imageView.layer.mask = maskShape
        dimmerView.layer.mask = mask_Dimmer
    }

    override func layoutSubviews()
    {
        super.layoutSubviews()

        // Let's go old school here...
        imageView.frame = self.bounds
        dimmerView.frame = self.bounds
        setupLayers()
    }
}

这样的事情会导致它只是捕捉到新的大小而不逐渐改变它的框架:

UIView.animate(withDuration: 1.0)
{
    self.headerView.arcHeight       = self.new_headerView_arcHeight
    self.headerView.frame           = self.new_headerView_frame
}

我认为这一定与我使用 CALayers 的事实有关,但我对幕后发生的事情了解得还不够。

编辑: 这是我用来创建圆弧路径的函数:

class func createHorizontalArcPath(_ startPoint:CGPoint, width:CGFloat, arcHeight:CGFloat, closed:Bool = false) -> CGMutablePath
    {
        // http://www.raywenderlich.com/33193/core-graphics-tutorial-arcs-and-paths

        let arcRect = CGRect(x: startPoint.x, y: startPoint.y-arcHeight, width: width, height: arcHeight)

        let arcRadius = (arcRect.size.height/2) + (pow(arcRect.size.width, 2) / (8*arcRect.size.height));
        let arcCenter = CGPoint(x: arcRect.origin.x + arcRect.size.width/2, y: arcRect.origin.y + arcRadius);

        let angle = acos(arcRect.size.width / (2*arcRadius));
        let startAngle = CGFloat(M_PI)+angle // (180 degrees + angle)
        let endAngle = CGFloat(M_PI*2)-angle // (360 degrees - angle)
        //        let startAngle = radians(180) + angle;
        //        let endAngle = radians(360) - angle;

        let path = CGMutablePath();
        path.addArc(nil, x: arcCenter.x, y: arcCenter.y, radius: arcRadius, startAngle: startAngle, endAngle: endAngle, clockwise: false);
        if(closed == true)
        {path.addLineTo(nil, x: startPoint.x, y: startPoint.y);}
        return path;
    }

奖励: 将 arcHeight 属性设置为 0 将导致不绘制白线。为什么?

【问题讨论】:

    标签: swift animation uiview calayer uiviewanimation


    【解决方案1】:

    Path 属性无法设置动画。你必须以不同的方式处理问题。您可以“立即”绘制弧线,任何弧线,这样就告诉我们需要手动处理动画。如果您预计整个绘制过程需要 3 秒,那么您可能希望将该过程拆分为 1000 个部分,并每 0.3 毫秒调用弧绘制函数 1000 次,以从起点重新绘制弧到当前点。

    【讨论】:

      【解决方案2】:

      self.headerView.arcHeight 不是动画属性。只有 UIView 自己的属性是可动画的

      你可以这样做

      let displayLink = CADisplayLink(target: self, selector: #selector(update))
      displayLink.addToRunLoop(NSRunLoop.currentRunLoop(), forMode: NSDefaultRunLoopMode
      
      let expectedFramesPerSecond = 60
      
      
      var diff : CGFloat = 0
      func update() {
          let diffUpdated = self.headerView.arcHeight - self.new_headerView_arcHeight
          let done = (fabs(diffUpdated) < 0.1)
          if(!done){
              self.headerView.arcHeight -= diffUpdated/(expectedFramesPerSecond*0.5)
              self.setNeedsDisplay()
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2023-04-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-08-19
        • 1970-01-01
        • 2013-05-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多