如何沿一条线“折叠” 3D UIView?
Posted
技术标签:
【中文标题】如何沿一条线“折叠” 3D UIView?【英文标题】:How can I "Fold" a UIView in 3D along a line? 【发布时间】:2020-05-05 09:33:59 【问题描述】:如果你有一个 UIView
想象一下视图充满了文本、动画、猫照片、渐变,或者实际上是 UIView 可以充满的任何东西。
在 3D 中很容易将其向左倾斜
或回到右边..
(顺便说一句,我强烈推荐完美的OHCubeView
ohcubeview,当你必须做那种事情时。)
但我想沿接缝创建一个视图,并让一侧向左后退,另一侧向后退向右..
!
我的第一个解决方案很简单:无论视图是什么(用户正在输入文本、猫的照片等等),只需完美地复制它,然后以显而易见的方式解决问题。
这很不雅,但确实有效。
但是……
我想像这样折叠的东西是...一个 SKView 里面有一个发射器。
粒子发射器当然是随机的,虽然两个相同的发射器看起来很相似,但这并不是真正的解决方案。
基本上,如果有人知道如何将 UIView“复制”到另一个、嗯、视图或其他东西......
问题会解决的。
例如,对于游戏引擎中的此类问题,您只需将“虚拟相机”(无论您最喜欢的游戏引擎中的概念是什么)放在有问题的事物上,然后将另一半或其他任何东西放在任何地方你想要,但是,我不知道在 ios 中的类比!
有人对此有意见吗:
【问题讨论】:
不完全是你想要的,但你也许可以从这个developer.apple.com/documentation/quartzcore/careplicatorlayer中破解一些东西 这不是您可以与 CADisplayLink 一起使用的东西吗?即一个页面是真正的渲染,第二个页面只是在每个屏幕刷新率下复制原始页面的快照。 @DanielGalasko ,你真的应该把它当作任何答案,否则赏金将被浪费 真正令人着迷的事情,@Aris(也许应该将其作为任何形式的答案,因为否则赏金将被浪费) 【参考方案1】:我可以想到两种方法。
1) 弯道法
您可以使用 SKWarpGeometry 弯曲您的场景。从语义上讲,您会在游戏场景中设想一个 2 x 2 象限网格,由 3 x 3 点数据结构描述。你会稍微移动左右边缘,并使它们更小。而且你会让中心垂直边缘更长一点。
Apple 的文档还提到,基本上任何类型的节点(包括 SKEmitterNode)都可以通过将其放入 SKEffectNode 并对其应用扭曲来进行扭曲。见:https://developer.apple.com/documentation/spritekit/skwarpgeometrygrid/animate_the_warping_of_a_sprite?language=objc。
我们所要做的就是让一个效果节点成为一切的根节点,并对其应用扭曲。现在,这行得通,但只要其中有一个发射器,经线就会开始像癫痫一样抽搐。我认为这是一个错误。无论如何,我都会包含代码,以防它稍后修复,因为它应该根据文档工作。
let sourcePoints = [
// bottom row: left, center, right
vector_float2(0.0, 0.0),
vector_float2(0.5, 0.0),
vector_float2(1.0, 0.0),
// middle row: left, center, right
vector_float2(0.0, 0.5),
vector_float2(0.5, 0.5),
vector_float2(1.0, 0.5),
// top row: left, center, right
vector_float2(0.0, 1.0),
vector_float2(0.5, 1.0),
vector_float2(1.0, 1.0)
]
var destinationPoints = sourcePoints
// Bring lefts in, and make the edge shorter
destinationPoints[0] = vector_float2(0.1, 0.1)
destinationPoints[3] = vector_float2(0.1, 0.4)
destinationPoints[6] = vector_float2(0.1, 0.9)
// Bring rights in, and make the edge shorter
destinationPoints[2] = vector_float2(0.9, 0.1)
destinationPoints[5] = vector_float2(0.9, 0.4)
destinationPoints[8] = vector_float2(0.9, 0.9)
// Make the center edge longer
destinationPoints[1] = vector_float2(0.5, -0.2)
destinationPoints[4] = vector_float2(0.5, 0.7)
destinationPoints[7] = vector_float2(0.5, 1.2)
// Need to set the no warp geometry first
effectNode.warpGeometry = SKWarpGeometryGrid(columns: 2, rows: 2)
let newGeometry = SKWarpGeometryGrid(columns: 2, rows: 2,
sourcePositions: sourcePoints,
destinationPositions: destinationPoints)
effectNode.run(SKAction.warp(to: newGeometry, duration: 1.0)!)
使用的来源:
https://www.hackingwithswift.com/example-code/games/how-to-warp-a-sprite-using-skwarpgeometrygrid
http://sound-of-silence.com/?page=blog&sort=recent&count=0
2) Live Copy 方法
这更简单,代码更少,用途更广泛;你可以在这个雷曼传奇关卡https://youtu.be/7m5YQrucis8?t=1226 中做类似的事情。此外,它还有效。
基本上,我们的主 SKView 显示主场景。我们将添加一个显示复制场景的副本 SKView。复制场景中只有一个精灵节点。 在主场景的更新循环中,我们将抓取场景的扁平纹理并将其分配给复制场景中的精灵节点。作为场景委托的对象可以进入这个更新循环,因此我们将使视图控制器符合 SKSceneDelegate。
class Playground_VC: UIViewController, SKSceneDelegate
var sprite: SKSpriteNode?
override func loadView()
self.view = SKView()
var skView: SKView
return self.view as! SKView
override func viewWillAppear(_ animated: Bool)
let scene = SKScene(size: self.skView.bounds.size)
scene.backgroundColor = UIColor.clear
scene.delegate = self
self.skView.presentScene(scene)
let e = newEmitter()
scene.addChild(e)
e.position = self.skView.midPoint
self.createCopy()
func update(_ currentTime: TimeInterval, for scene: SKScene)
// Grab the root texture and assign to the sprite that's in the copy scene
let t = self.skView.texture(from: self.skView.scene!)
self.sprite?.texture = t
func createCopy()
// Make a new SKView that's 4 times smaller
var f = self.skView.bounds
f.size.height *= 0.4
f.size.width *= 0.4
let copy = SKView(frame: f)
copy.presentScene(SKScene(size: f.size))
self.view.addSubview(copy)
let sprite = SKSpriteNode(texture: nil, size: f.size)
copy.scene?.addChild(sprite)
self.sprite = sprite
sprite.position = CGPoint(x: f.size.width / 2, y: f.size.height / 2)
func newEmitter() -> SKEmitterNode
return SKEmitterNode(fileNamed: "Particle.sks")!
您在主 SKView 上执行的任何操作,例如在 3D 空间中倾斜它,都不应影响副本的外观,因为游戏场景不受这些操作的影响。
【讨论】:
太棒了! (你知道,我认为这基本上是 SK 案例的 2 个完整解决方案 - 太棒了。我想知道 UIView 案例是否真的有任何方法)【参考方案2】:我可以想到几件事来尝试:
将 UIView 的内容复制到某处:您可以截屏:
...到图像:
UIGraphicsBeginImageContextWithOptions(view.bounds.size, false,
UIScreen.mainScreen().scale)
view.drawViewHierarchyInRect(view.bounds, afterScreenUpdates: true)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
...或视图(根据Apple的文档比上述更快):
func snapshotView(afterScreenUpdates afterUpdates: Bool) -> UIView?
最后,这个项目相当老旧,无人维护,并且是用 Obj-C 编写的,但也许它能让你了解他们是如何为你自己做的:
https://github.com/honcheng/PaperFold-for-iOS
【讨论】:
啊,九,谢谢你的回答,但我当然想做一个真实的视图!一个 UIView。 (渲染静态图像并弯曲它真的很简单!)【参考方案3】:这篇文章没有回答如何在 3D 中折叠 UIView。 OP提到:
“本质上,如果有人知道如何将一个 UIView “复制”到另一个, 嗯,观点,什么的……问题就解决了。”
如果复制视图可以解决这个问题,那么你应该看看这个question 我试过这个answer,它工作正常。我试了一下:
上述输出的代码:
import UIKit
class ViewController: UIViewController
@IBOutlet weak var textField: UITextField!
@IBOutlet weak var rightView: UIView!
@IBOutlet weak var leftView: UIView!
override func viewDidLoad()
super.viewDidLoad()
textField.addTarget(self, action: #selector(didChange(textField:)), for: .editingChanged)
@objc func didChange(textField: UITextField)
rightView.subviews.forEach$0.removeFromSuperview()
let v = (try? leftView.copyObject()) as? UIView ?? UIView()
rightView.addSubview(v)
v.translatesAutoresizingMaskIntoConstraints = true
NSLayoutConstraint.activate([
v.topAnchor.constraint(equalTo: rightView.topAnchor),
v.leadingAnchor.constraint(equalTo: rightView.leadingAnchor),
v.bottomAnchor.constraint(equalTo: rightView.bottomAnchor),
v.trailingAnchor.constraint(equalTo: rightView.trailingAnchor)
])
extension NSObject
func copyObject<T:NSObject>() throws -> T?
let data = try NSKeyedArchiver.archivedData(withRootObject:self, requiringSecureCoding:false)
return try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as? T
【讨论】:
@downvoter,当您对答案投反对票时,请提及其背后的原因。也许这很明显.. 但它仍然有助于改善某人的答案以上是关于如何沿一条线“折叠” 3D UIView?的主要内容,如果未能解决你的问题,请参考以下文章