- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我试图将 View 元素放置在 UIImageView 中,然后对 UIImageView 的宽度进行动画处理以填充 super View 。我正在使用自动布局约束来定位 View 并为其设置动画。在动画开始时,圆正好位于 superView 的 centerX 的一半处:
self.circleXConstraint = NSLayoutConstraint(item: self.circle, attribute: .centerX, relatedBy: .equal, toItem: self.bodyView, attribute: .centerX, multiplier: 0.5, Constant: 0)
但是当动画开始时,圆圈会偏离正确的位置(两条线交叉的位置)。
我花了几天时间试图找出为什么自动布局不能保持圆形 View 正确定位,但无法找出原因。
Xcode Playground 的完整代码可以在这里下载: https://github.com/imyrvold/Animate-body
Xcode Playground swift 代码:
import UIKit
import PlaygroundSupport
class ViewController : UIViewController {
lazy var bodyView: UIImageView = {
let bodyView = UIImageView()
bodyView.contentMode = .scaleAspectFill
bodyView.image = self.image
bodyView.backgroundColor = .yellow
return bodyView
}()
lazy var image: UIImage? = {
UIImage(named: "body")
}()
lazy var button: UIButton = {
let button = UIButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
button.setTitle("Start", for: .normal)
button.addTarget(self, action: #selector(headZoom), for: .touchDown)
button.titleLabel?.textColor = .black
button.backgroundColor = .lightGray
return button
}()
lazy var circle: BodyAreaView = {
let circle = BodyAreaView(frame: CGRect(x: 0, y: 0, width: 48, height: 48))
circle.backgroundColor = .clear
return circle
}()
// bodyView constraints
var topAnchorConstraint: NSLayoutConstraint!
var centerXAnchorConstraint: NSLayoutConstraint!
var aspectConstraint: NSLayoutConstraint!
var superWidthConstraint: NSLayoutConstraint!
var iphoneHeightConstraint: NSLayoutConstraint!
var iphoneWidthConstraint: NSLayoutConstraint!
// circle constraints
var circleHeightConstraint: NSLayoutConstraint!
var circleXConstraint: NSLayoutConstraint!
var circleYConstraint: NSLayoutConstraint!
var circleAspectConstraint: NSLayoutConstraint!
override func loadView() {
let view = UIView()
view.backgroundColor = .white
self.view = view
view.addSubview(self.bodyView)
self.bodyView.addSubview(self.circle)
view.addSubview(self.button)
self.view.translatesAutoresizingMaskIntoConstraints = false
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
self.setBodyViewConstraints()
self.setFibroConstraints()
self.bodyView.translatesAutoresizingMaskIntoConstraints = false
self.circle.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
self.topAnchorConstraint,
self.centerXAnchorConstraint,
self.aspectConstraint,
self.iphoneWidthConstraint
])
NSLayoutConstraint.activate([
self.circleHeightConstraint,
self.circleXConstraint,
self.circleYConstraint,
self.circleAspectConstraint
])
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
}
@objc func headZoom() {
self.superWidthConstraint.isActive = true
print("body view before: \(self.bodyView.bounds)")
print("circle origin before: \(self.circle.frame.origin)")
UIView.animate(withDuration: 3, animations: {
self.view.layoutIfNeeded()
}) { _ in
print("body view after: \(self.bodyView.bounds)")
print("circle frame after: \(self.circle.frame)")
}
}
func setBodyViewConstraints() {
self.topAnchorConstraint = self.bodyView.topAnchor.constraint(equalTo: self.view.topAnchor)
self.centerXAnchorConstraint = self.bodyView.centerXAnchor.constraint(equalTo: self.view.centerXAnchor)
self.aspectConstraint = self.bodyView.heightAnchor.constraint(equalTo: self.bodyView.widthAnchor, multiplier: 1388/408.0)
self.superWidthConstraint = self.bodyView.widthAnchor.constraint(equalTo: self.view.widthAnchor, multiplier: 1)
self.iphoneHeightConstraint = self.bodyView.heightAnchor.constraint(equalToConstant: 633)
self.iphoneWidthConstraint = self.bodyView.widthAnchor.constraint(equalToConstant: 186)
}
func setFibroConstraints() {
self.circleHeightConstraint = self.circle.heightAnchor.constraint(equalTo: self.bodyView.heightAnchor, multiplier: 0.05)
self.circleXConstraint = NSLayoutConstraint(item: self.circle, attribute: .centerX, relatedBy: .equal, toItem: self.bodyView, attribute: .centerX, multiplier: 0.5, constant: 0)
self.circleYConstraint = NSLayoutConstraint(item: self.circle, attribute: .centerY, relatedBy: .equal, toItem: self.bodyView, attribute: .centerY, multiplier: 0.5, constant: 0)
self.circleAspectConstraint = self.circle.heightAnchor.constraint(equalTo: self.circle.widthAnchor, multiplier: 1)
}
}
let viewController = ViewController()
viewController.preferredContentSize = CGSize(width: 375, height: 812)
PlaygroundPage.current.liveView = viewController
我已经更新了 Xcode Playground,现在它将按照建议以 .cyan 颜色显示圆形 View 的背景。
听起来,更新 UIView 子类的绘图代码是一项简单的任务,但在堆栈溢出上搜索解决方案几个小时后,没有一篇文章有任何帮助。看起来圆圈拒绝重新绘制自身,即使 View 本身的边界发生变化。
我简化了 UIView 子类代码,以便更容易理解绘图的工作原理。我很乐意为此获得任何帮助。我看过的许多帖子都建议重写layoutSubviews,将子层框架设置为 View 层边界,但这在这种情况下不起作用。一定是我忽略了一些东西。
这是 BodyAreaView 的代码,简化为仅绘制圆环:
import UIKit
public class BodyAreaView: UIView {
var ringColor: UIColor = .black
lazy var ringLayer: CAShapeLayer = {
let ringLayer = CAShapeLayer()
let ringPath = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 4, dy: 4))
ringLayer.fillColor = UIColor.clear.cgColor
ringLayer.strokeColor = UIColor.black.cgColor
ringLayer.path = ringPath.cgPath
ringLayer.name = "ring"
ringLayer.needsDisplayOnBoundsChange = true
return ringLayer
}()
override public func draw(_ rect: CGRect) {
self.layer.addSublayer(self.ringLayer)
}
override public func layoutSubviews() {
guard let sublayers = self.layer.sublayers else { return }
for layer in sublayers {
layer.frame = layer.bounds
}
}
}
最佳答案
首先,您没有考虑到布局可能会出现多次。因此,在您的override func viewDidLayoutSubviews()
中,您会一遍又一遍地重新激活相同的约束。这意味着当你说
self.superWidthConstraint.isActive = true
您遇到了约束冲突,因为您的iphoneWidthConstraint
仍然处于事件状态:您要求两者旧的宽度约束和新的宽度约束。事实上,您看到任何宽度变化纯粹是运气。
解决办法如下。首先,编写带有标志的 layout
代码,以便仅初始化所有内容一次:
var didInitialLayout = false
override func viewDidLayoutSubviews() {
if didInitialLayout { return } // *
didInitialLayout = true //
// ...
其次,在激活新的宽度限制之前停用旧的宽度限制:
@objc func headZoom() {
self.iphoneWidthConstraint.isActive = false // *
self.superWidthConstraint.isActive = true
// ...
好的,现在冲突已经消失了。
现在我们可以专注于绘图代码了!该代码(您的 BodyAreaView)有很多问题,我不知道从哪里开始。但是,总结一下:
override public func draw(_ rect: CGRect) {
self.layer.addSublayer(self.ringLayer)
}
这里我们有一个典型的错误:绘制
,而您不绘制。相反,您会做一些完全无关的事情:添加一个层。这总是很危险的,因为draw
可以被调用很多次;在最坏的情况下,您最终可能会一遍又一遍地添加此层。 (在这里,这可能不会发生,但这只是运气。)
下一个:
override public func layoutSubviews() {
您似乎认为在动画播放期间您会多次收到该消息。你不。而且你忘记调用 super
这可能是灾难性的。
最后,您对形状图层的使用:
lazy var ringLayer: CAShapeLayer = {
let ringLayer = CAShapeLayer()
let ringPath = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 4, dy: 4))
ringLayer.fillColor = UIColor.clear.cgColor
ringLayer.strokeColor = UIColor.black.cgColor
ringLayer.path = ringPath.cgPath
ringLayer.name = "ring"
ringLayer.needsDisplayOnBoundsChange = true
return ringLayer
}()
首先,您的图层没有框架,因此没有大小。其次,此代码将仅运行一次。因此,您将获得一条路径,一次,然后就不会发生任何会改变路径大小的事情。当 View 发生变化时,ringPath 半径不会神奇地永远与 View 的大小相关联。它是按照一定的尺寸创建的,仅此而已。这就是为什么您实际上看不到路径发生变化的原因。 (使用needsDisplayOnBoundsChange
是完全无关的,因为你没有一个可以显示的图层!那将是你进行绘图的图层,而这并不是什么你有。)
好吧,最简单的解决方案是什么?我首先要做的就是完全丢弃图层并只绘制圆圈:
public class BodyAreaView: UIView {
override public func draw(_ rect: CGRect) {
let ringPath = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 4, dy: 4))
let con = UIGraphicsGetCurrentContext()!
con.addPath(ringPath.cgPath)
con.strokePath()
}
}
效果非常好,因为我们绘制了一次,从那时起我们就使用缓存的圆形绘图,它会随着 View 的增长而增长。如果我们再次绘制,我们将根据当时 View 的大小按比例绘制,因此圆圈看起来总是正确的。
好的,但这并不能真正回答您最初的问题,即如何使用图层来做到这一点。好吧,答案又是:不要使用形状图层,因为那不是自动重绘图层。使用自定义图层执行此操作,该图层知道如何根据当前边界大小将自身重新绘制为圆形。例如:
public class BodyAreaView: UIView {
class Circle : CALayer {
override func draw(in con: CGContext) {
let ringPath = UIBezierPath(ovalIn: self.bounds.insetBy(dx: 4, dy: 4))
con.addPath(ringPath.cgPath)
con.strokePath()
}
}
let circle = Circle()
public override init(frame: CGRect) {
super.init(frame:frame)
self.circle.needsDisplayOnBoundsChange = true
self.circle.frame = self.bounds
self.layer.addSublayer(self.circle)
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
public override func layoutSubviews() {
super.layoutSubviews()
if let layers = self.layer.sublayers {
for lay in layers {
lay.frame = self.bounds
}
}
}
}
但是,请注意,这会导致圆圈的大小在动画开始时跳跃。它跳到正确的位置,即动画结束时圆圈需要出现的位置。那是因为你没有获得图层的自动布局动画。这就是为什么我更喜欢第一个解决方案;至少在那里,圆圈随着视野而变大。
但是您可以通过单独设置图层大小的动画来解决该问题。这留给读者作为练习。 :)
关于swift - 在 Xcode Playground 中制作动画时,位置元素与 super View 成比例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51694042/
我正在观看来自 iTunes U. 的视频讲座,其中讲师展示了如何使用 Xcode 的调试器部分并能够将该窗口从主窗口拉开。 不幸的是,视频显示了如何在 Xcode3 中执行此操作的说明。如何在 Xc
现在我正在开发一个使用 Markdown 的静态网站。我不想在 SublimeText 中编辑文件,而是想在 Xcode 中编辑它们。我可以在 Xcode 中打开各个文件,但我想打开整个静态站点目录并
现在我正在开发一个使用 Markdown 的静态网站。我不想在 SublimeText 中编辑文件,而是想在 Xcode 中编辑它们。我可以在 Xcode 中打开各个文件,但我想打开整个静态站点目录并
我真的厌倦了其他用户的项目错误,因为 XCode 的默认类引用类型是“相对于 XCode 文件夹”。我知道您可以将其更改为“相对于封闭组”,这就是我们的项目,但是任何时候有人创建一个新的类文件并且忘记
我对 Xcode 一无所知,除了它是来自 Apple 的开发人员界面,它实际上来自 OSX CD,它也用于创建 iPhone 应用程序。 我也知道它有一个 Applications 文件夹,里面装满了
更新 Xcode(从 7.2 到 7.2.1)后,XcodeServer 无法运行 Xcode Service。当我选择更新的 Xcode 时,提示消息:Enable Accessibility ac
我很难找到有关新 XCode 布局的信息。如何查看源文件的反汇编,而不仅仅是 C++ 代码? 最佳答案 在 Xcode 4.2(和 Xcode 5)中,您可以通过以下方式查看文件的汇编或反汇编: 显示
我在 XCode 中为我的项目添加了一个文件夹并将其命名为 Themes,它将用于存储我的 iPad 应用程序的主题。在那下面我有红色、蓝色等等。 它们出现在 XCode 中。但是当我查看物理文件夹时
作为我之前 How can I create a reference cycle using dispatchQueues? 的后续: 对于强引用(会造成泄漏,但不是循环引用),例如Timer、Dis
我有一台 macbook air,我去商店安装 Xcode 7.3 并点击更新 App 按钮,然后出现了一个加载图标,但等待 30 分钟后什么也没发生。我从商店成功安装了 simple recorde
我的项目现在有一个project.xcworkspace/xcshareddata目录。 Xcode在project.xcworkspace/xcshareddata目录中存储什么? 最佳答案 xcs
我不经常使用 XCode 进行开发,最近 MacPorts 告诉我我需要获得 3.1 才能使包工作。我去了苹果,他们为我提供了最新版本,结果只适用于 OSX 6。因为我有 OSX 5,它对我没有任何帮
有谁知道如何强制 Xcode 停止运行。我更新到 6.2 但现在它不能正常工作。我需要退出 Xcode 才能完成更新或卸载并重新安装。菜单中的相当 Xcode 不可用。无法将其拖到垃圾箱,因为它说它仍
我最近从我的项目中删除了一些旧的测试类并删除了文件。正如预期的那样,文件被移到垃圾箱并在 git 中显示为已删除。 不幸的是,测试类和它们定义的测试用例继续出现在测试导航器中。我已经尝试过常见的疑点,
Xcode 中的场景编辑器显示大小但不显示单位。它是以英寸为单位的尺寸还是以米为单位的尺寸? 例如,当我选择一个节点时,例如一个盒子,当我在节点检查器中检查它的属性并在“边界框”旁边的“变换”部分检查
只想知道 xcode 项目中事件可执行文件的目的是什么? 谢谢 索拉布 最佳答案 自己找到了..希望它会帮助某人。 您可以在其中运行和测试软件产品的可执行环境。可执行环境定义了应该用来运行产品的程序。
刚回到我的办公 table ,Xcode 一直在文件中突出显示/强调空白区域。如果我输入一些东西它就会消失,但如果我切换标签并回来,它们会重新出现。 更新:这不是由“显示隐形”选项引起的。那看起来不一
我首先注意到这一点 Cmd-/偶尔拒绝为注释取消注释的代码行工作。但是现在(刚刚使用 XCode 4.1,虽然这可能无关)它发生在 Cmd-B建立。 我无法弄清楚我正在做什么导致这种情况,或者是否有任
我不太了解Xcode 4的工作区的实用程序。它们的用途是什么,它们如何帮助Xcode进行开发? 最佳答案 例如。您有一个在两个应用程序中使用的库。您很可能为此库拥有一个自己的项目,对吗?现在,您可以自
我正在关注一个应用程序教程,它说: “接下来,在 Xcode 应用程序项目目录的根目录下创建一个空文件,并调用该文件 Podfile。使用您喜欢的文本编辑器打开它,然后在其中粘贴以下行:” 我很困惑这
我是一名优秀的程序员,十分优秀!