Vin*_*oil 15 macos cocoa nsview swift swift2
所以我创建了一个NSButton有一个漂亮的单选按钮的自定义,但我遇到了一个非常奇怪的错误.
我的单选按钮在操场上看起来不错,但是当我将它添加到我的项目时,它看起来很奇怪.
这是截图:
左 =在操场上.
右 =在我的项目中.
正如你所看到的,在右边(在我的项目中),蓝点看起来很可怕,它不是光滑的,对于白色圆圈来说是相同的(在深色背景下它不太明显).
在我的项目中,即使我的main(_containerLayer_)上的属性设置为NSShadow,我CALayer的也会被翻转.geometryFlippedCALayertrue- > 固定:见@Bannings的回答.
import AppKit
extension NSColor {
static func colorWithDecimal(deviceRed deviceRed: Int, deviceGreen: Int, deviceBlue: Int, alpha: Float) -> NSColor {
return NSColor(
deviceRed: CGFloat(Double(deviceRed)/255.0),
green: CGFloat(Double(deviceGreen)/255.0),
blue: CGFloat(Double(deviceBlue)/255.0),
alpha: CGFloat(alpha)
)
}
}
extension NSBezierPath {
var CGPath: CGPathRef {
return self.toCGPath()
}
/// Transforms the NSBezierPath into a CGPathRef
///
/// :returns: The transformed NSBezierPath
private func toCGPath() -> CGPathRef {
// Create path
let path = CGPathCreateMutable()
var points = UnsafeMutablePointer<NSPoint>.alloc(3)
let numElements = self.elementCount
if numElements > 0 {
var didClosePath = true
for index in 0..<numElements {
let pathType = self.elementAtIndex(index, associatedPoints: points)
switch pathType {
case .MoveToBezierPathElement:
CGPathMoveToPoint(path, nil, points[0].x, points[0].y)
case .LineToBezierPathElement:
CGPathAddLineToPoint(path, nil, points[0].x, points[0].y)
didClosePath = false
case .CurveToBezierPathElement:
CGPathAddCurveToPoint(path, nil, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y)
didClosePath = false
case .ClosePathBezierPathElement:
CGPathCloseSubpath(path)
didClosePath = true
}
}
if !didClosePath { CGPathCloseSubpath(path) }
}
points.dealloc(3)
return path
}
}
class RadioButton: NSButton {
private var containerLayer: CALayer!
private var backgroundLayer: CALayer!
private var dotLayer: CALayer!
private var hoverLayer: CALayer!
required init?(coder: NSCoder) {
super.init(coder: coder)
self.setupLayers(radioButtonFrame: CGRectZero)
}
override init(frame frameRect: NSRect) {
super.init(frame: frameRect)
let radioButtonFrame = CGRect(
x: 0,
y: 0,
width: frameRect.height,
height: frameRect.height
)
self.setupLayers(radioButtonFrame: radioButtonFrame)
}
override func drawRect(dirtyRect: NSRect) {
}
private func setupLayers(radioButtonFrame radioButtonFrame: CGRect) {
//// Enable view layer
self.wantsLayer = true
self.setupBackgroundLayer(radioButtonFrame)
self.setupDotLayer(radioButtonFrame)
self.setupHoverLayer(radioButtonFrame)
self.setupContainerLayer(radioButtonFrame)
}
private func setupContainerLayer(frame: CGRect) {
self.containerLayer = CALayer()
self.containerLayer.frame = frame
self.containerLayer.geometryFlipped = true
//// Mask
let mask = CAShapeLayer()
mask.path = NSBezierPath(ovalInRect: frame).CGPath
mask.fillColor = NSColor.blackColor().CGColor
self.containerLayer.mask = mask
self.containerLayer.addSublayer(self.backgroundLayer)
self.containerLayer.addSublayer(self.dotLayer)
self.containerLayer.addSublayer(self.hoverLayer)
self.layer!.addSublayer(self.containerLayer)
}
private func setupBackgroundLayer(frame: CGRect) {
self.backgroundLayer = CALayer()
self.backgroundLayer.frame = frame
self.backgroundLayer.backgroundColor = NSColor.whiteColor().CGColor
}
private func setupDotLayer(frame: CGRect) {
let dotFrame = frame.rectByInsetting(dx: 6, dy: 6)
let maskFrame = CGRect(origin: CGPointZero, size: dotFrame.size)
self.dotLayer = CALayer()
self.dotLayer.frame = dotFrame
self.dotLayer.shadowColor = NSColor.colorWithDecimal(deviceRed: 46, deviceGreen: 146, deviceBlue: 255, alpha: 1.0).CGColor
self.dotLayer.shadowOffset = CGSize(width: 0, height: 2)
self.dotLayer.shadowOpacity = 0.4
self.dotLayer.shadowRadius = 2.0
//// Mask
let maskLayer = CAShapeLayer()
maskLayer.path = NSBezierPath(ovalInRect: maskFrame).CGPath
maskLayer.fillColor = NSColor.blackColor().CGColor
//// Gradient
let gradientLayer = CAGradientLayer()
gradientLayer.frame = CGRect(origin: CGPointZero, size: dotFrame.size)
gradientLayer.colors = [
NSColor.colorWithDecimal(deviceRed: 29, deviceGreen: 114, deviceBlue: 253, alpha: 1.0).CGColor,
NSColor.colorWithDecimal(deviceRed: 59, deviceGreen: 154, deviceBlue: 255, alpha: 1.0).CGColor
]
gradientLayer.mask = maskLayer
//// Inner Stroke
let strokeLayer = CAShapeLayer()
strokeLayer.path = NSBezierPath(ovalInRect: maskFrame.rectByInsetting(dx: 0.5, dy: 0.5)).CGPath
strokeLayer.fillColor = NSColor.clearColor().CGColor
strokeLayer.strokeColor = NSColor.blackColor().colorWithAlphaComponent(0.12).CGColor
strokeLayer.lineWidth = 1.0
self.dotLayer.addSublayer(gradientLayer)
self.dotLayer.addSublayer(strokeLayer)
}
private func setupHoverLayer(frame: CGRect) {
self.hoverLayer = CALayer()
self.hoverLayer.frame = frame
//// Inner Shadow
let innerShadowLayer = CAShapeLayer()
let ovalPath = NSBezierPath(ovalInRect: frame.rectByInsetting(dx: -10, dy: -10))
let cutout = NSBezierPath(ovalInRect: frame.rectByInsetting(dx: -1, dy: -1)).bezierPathByReversingPath
ovalPath.appendBezierPath(cutout)
innerShadowLayer.path = ovalPath.CGPath
innerShadowLayer.shadowColor = NSColor.blackColor().CGColor
innerShadowLayer.shadowOpacity = 0.2
innerShadowLayer.shadowRadius = 2.0
innerShadowLayer.shadowOffset = CGSize(width: 0, height: 2)
self.hoverLayer.addSublayer(innerShadowLayer)
//// Inner Stroke
let strokeLayer = CAShapeLayer()
strokeLayer.path = NSBezierPath(ovalInRect: frame.rectByInsetting(dx: -0.5, dy: -0.5)).CGPath
strokeLayer.fillColor = NSColor.clearColor().CGColor
strokeLayer.strokeColor = NSColor.blackColor().colorWithAlphaComponent(0.22).CGColor
strokeLayer.lineWidth = 2.0
self.hoverLayer.addSublayer(strokeLayer)
}
}
let rbFrame = NSRect(
x: 87,
y: 37,
width: 26,
height: 26
)
let viewFrame = CGRect(
x: 0,
y: 0,
width: 200,
height: 100
)
let view = NSView(frame: viewFrame)
view.wantsLayer = true
view.layer!.backgroundColor = NSColor.colorWithDecimal(deviceRed: 40, deviceGreen: 40, deviceBlue: 40, alpha: 1.0).CGColor
let rb = RadioButton(frame: rbFrame)
view.addSubview(rb)
Run Code Online (Sandbox Code Playgroud)
我在我的项目和操场上使用完全相同的代码.
这是一个包含游乐场和项目的拉链.
需要明确的是:我想知道为什么圆形图画在操场上是平滑的而不是在项目中.(参见@Bannings的答案,截图更明显)
Fan*_*ude 16
花了很多时间,但我想我终于想出了一切或几乎所有的东西.
首先是一些科学:圆形或弧形不能通过贝塞尔曲线表示.这是Bézier曲线的属性,如下所述:https://en.wikipedia.org/wiki/Bézier_curve
因此,在使用时,NSBezierPath(ovalInRect:)您实际上会生成近似圆的Bézier曲线.这可能导致外观和形状呈现方式的差异.在我们的情况下,这应该不是问题,因为两个Bézier曲线(Playground中的曲线和实际OS X项目中的曲线)之间存在差异,但我仍然觉得有趣的是,如果您认为圆圈不是足够完美.
其次如本问题所述(如何使用CAShapeLayer和UIBezierPath绘制平滑圆),根据路径的使用位置,抗锯齿将应用于路径的方式有所不同.NSView's drawRect:是所在的路径抗锯齿将是这个地方最好,并CAShapeLayer作为最糟糕的.
另外我发现CAShapeLayer文档有一条说明:
形状光栅化可能有利于速度而不是准确性.例如,具有多个相交路径段的像素可能无法给出精确结果.
Glen Low对我之前提到的问题的回答似乎在我们的案例中运作良好:
layer.rasterizationScale = 2.0 * self.window!.screen!.backingScaleFactor;
layer.shouldRasterize = true;
Run Code Online (Sandbox Code Playgroud)
看到这里的差异:
另一个解决方案是利用角半径而不是Bézier路径来模拟圆,这次它非常准确:
最后,我对Playground和真实OS X项目之间差异的假设是Apple配置了Playground以便关闭一些优化,因此即使是使用绘制的路径也CAShapeLayer可以获得最佳的抗锯齿效果.毕竟你是原型制作,表演并不是特别重要,特别是在绘画操作上.
我不确定是对的,但我认为这并不奇怪.如果有人有任何来源我很乐意添加它.
对我来说,如果你真的需要最好的圆圈,最好的解决方案是使用角半径.
另外正如@Bannings在这篇文章的另一个答案中所述.由于操场在不同的坐标系中渲染,因此阴影被反转.看到他解决这个问题的答案.
我只是通过替换此行代码修复它:
self.dotLayer.shadowOffset = CGSize(width: 0, height: 2)
Run Code Online (Sandbox Code Playgroud)
有:
self.dotLayer.shadowOffset = CGSize(width: 0, height: -2)
Run Code Online (Sandbox Code Playgroud)
并替换innerShadowLayer.shadowOffset = CGSize(width: 0, height: 2)为:
innerShadowLayer.shadowOffset = CGSize(width: 0, height: -2)
Run Code Online (Sandbox Code Playgroud)
我想你会得到同样的结果:
左 =在操场上.
右 =在我的项目中.
它似乎Playground显示了LLO坐标系中的bezier路径,您可以访问链接:https:
//forums.developer.apple.com/message/39277#39277
| 归档时间: |
|
| 查看次数: |
318 次 |
| 最近记录: |