这是一个示例视图:
我想计算一个CGPoint
的框架,我可以在不触及任何现有卡片的情况下生成另一张卡片(UIView
)。当然,它是可选的,因为视图可以充满卡片,因此没有免费的位置。
这就是我在屏幕上看到任何卡片以及我的功能现在的样子:
func freeSpotCalculator() -> CGPoint?{
var takenSpots = [CGPoint]()
for card in playableCards{
takenSpots.append(card.center)
}
}
我不知道从哪里开始以及如何在屏幕上计算随机CGPoint
。随机帧与屏幕上的卡片具有相同的width
和height
。
答案 0 :(得分:4)
这种天真的方法非常简单,但一旦屏幕填满就可能出现问题。生成随机CGPoint,其中x坐标介于0和屏幕宽度之间,y坐标介于0和屏幕高度之间。检查在该点处具有中心的矩形是否与任何现有视图相交。如果没有,你有随机的位置。
当问题出现时,屏幕开始填满。此时,您可能会在找到放置卡片的位置之前尝试许多随机点。您还可以达到不再适合的情况。你怎么知道你已经达到了?你的循环产生随机点会永远运行吗?
更智能的解决方案是跟踪屏幕上的可用空间。始终在这些自由空间内大致生成随机点。如果近似足够接近,您可以使用网格执行此操作。是否存在占用每个网格位置的卡?然后当最大的可用空间小于卡片矩形的大小时,你知道你已经完成了。这比天真的方法要多得多,但是当屏幕开始填满时它会更快,你肯定知道什么时候完成。
如果你知道你的屏幕空间总是超过卡可能占用的空间,那么天真的方法就可以了。
答案 1 :(得分:1)
您知道容器UIView的宽度和高度。并且,每张卡具有相同的宽度和高度。我会通过计算网格来解决这个问题。
即使您想要随机显示卡片,依靠网格也会为您提供标准化的中心阵列,您可以使用这些中心生成随机性的外观(将卡片放置在作为网格一部分的任意随机中心,例如)。
如果您要在任何随机位置放置卡片,您可能只想使用CGRectIntersectsRect(card1.frame, card2.frame)
来检测碰撞。
首先,让我们将卡片的宽度和高度存储为常数。
let cardWidth = card.bounds.size.width
let cardHeight = card.bounds.size.height
作为概念的基本证明,我们假设您的容器视图宽度为250磅。我们假设卡片宽度为5分。这意味着您可以在一行中容纳250/5 = 50张卡片,其中一行具有一张卡片的高度。
连续中心的数量=该行中的卡数。每个中心的距离相同。在下图中(如果我甚至可以称之为),[
和]
表示行的边缘。 -|-
代表一张卡片,其中|
是卡片的中心。
[ - | - - | - - | - - | - - | - ]
注意每个中心距离下一个中心是两个破折号。唯一的考虑因素是边缘旁边的中心距边缘一个点。就卡片而言,每个中心距离下一个中心是一整张卡片,边缘旁边的中心距离边缘半卡。
此模式表示特定行中任意卡片中心的x位置= (cardWidth / 2) + (the card index * cardWidth)
。实际上,这个伪方程也适用于y位置。
这里有一些使用这种方法创建一系列中心的Swift。
var centers = [CGPoint]()
let numberOfRows: CGFloat = containerView.bounds.size.height / cardHeight
let numberOfCardsPerRow: CGFloat = containerView.bounds.size.width / cardWidth
for row in 0 ..< Int(numberOfRows) {
for card in 0 ..< Int(numberOfCardsPerRow) {
// The row we are on affects the y values of all the centers
let yBasedOnRow = (cardHeight / 2) + (CGFloat(row) * cardHeight)
// The xBasedOnCard formula is effectively the same as the yBasedOnRow one
let xBasedOnCard = (cardWidth / 2) + (CGFloat(card) * cardWidth)
// Each possible center for this row gets appended to the centers array
centers.append(CGPoint(x: xBasedOnCard, y: yBasedOnRow))
}
}
此代码应为您的卡片创建一个中心网格。您可以围绕它构建一个函数,该函数返回一个随机中心,用于放置卡片并跟踪使用过的中心。
首先,我认为centers
数组可以构成一个矩阵([[CGPoint]]()
),用于更多逻辑存储点。
其次,此代码目前假设容器视图的宽度和高度可以被卡宽度和高度整除。例如,容器宽度为177,卡宽度为5将导致一些问题。代码可以通过多种不同的方式来解决。
答案 2 :(得分:1)
最简单/性能最佳解决方案是在网格内随机显示卡片。诀窍是使网格大于卡片大小,因此网格内的卡片位置将是随机的。
轻松检查哪个位置占用,卡片将在“随机”帧上。
1-创建一个集合视图控制器,其中包含您想要显示的卡的总数(让我们说..在屏幕上输入的最大卡?)
2-将原型单元尺寸设置为大于卡。如果卡是50x80,则单元格应为70x110。
3-将UIImageView添加到具有约束的单元格中,这将是您的卡片图像
4-创建一个UICollectionViewCell,使用一种在单元格内随机设置卡片框架的方法(修改约束)
完成!
没有卡的单元格根据需要没有图像或空单元格。因此,要添加一张新卡,只需在空单元格之间随机添加,并在单元格中添加随机坐标卡。
您的UICollectionViewCell会喜欢这个
class CardCollectionViewCell: UICollectionViewCell {
@IBOutlet weak var card: UIImageView!
override func awakeFromNib() {
super.awakeFromNib()
let newX = CGFloat(arc4random_uniform(UInt32(bounds.size.width-card.bounds.size.width+1)))
let newY = CGFloat(arc4random_uniform(UInt32(bounds.size.height-card.bounds.size.height+1)))
card.leftAnchor.constraintEqualToAnchor(leftAnchor, constant: newX).active = true
card.topAnchor.constraintEqualToAnchor(rightAnchor, constant: newY).active = true
}
}
你的Collections View Controller应该是这样的 Collection View Image
答案 3 :(得分:0)
正如我在图片中看到的那样,所有卡片都在视图的底部对齐。因此,如果您从0 to origin of your cards row - one card height
生成一个随机y位置,您只需根据视图的框架和卡片的大小获得CGPoint。
答案 4 :(得分:0)
如果您想在屏幕上随机放置卡片,可以执行以下操作:
func random() -> CGFloat {
return CGFloat(Float(arc4random()) / 0xFFFFFFFF)
}
func random(min: CGFloat, max: CGFloat) -> CGFloat {
return random() * (max - min) + min
}
let actualX = random(min: *whatever amount*, max: *whatever amount* )
let actualY = random(min: *Height of the first card*, max: *whatever amount* )
card.position = CGPoint(x: actualX, y: actualY )
然后将卡片随机放置在现有卡片上方。
答案 5 :(得分:0)
我不确定您是否打算有序地放置所有卡片。但如果你这样做,你可以这样做。
加载视图后,获取所有可能的卡位置,并将其与用作密钥的数字一起存储在地图中。然后,您可以生成一个从0到您存储在地图中的可能卡位置总数的随机数。然后,每次占据一个位置,清除地图上的值。
答案 6 :(得分:0)
您可以尝试使用CAShapeLayer和UIBezierPath。
我使用上述逻辑在swift中创建了一个示例项目。
https://github.com/mcabasheer/find-free-space-in-uiview
您可以更改新视图的宽度和高度。我添加了一个条件,在尝试50次后停止寻找下一个可用空间。这有助于避免无限循环。
正如@davecom强调的那样,使用随机数添加新视图会浪费空间,并且您将很快耗尽空间。如果您能够维护可用空间,则可以添加更多子视图。