Swift中for循环创建卡片UIButton时生成数量与预期不符问题
问题:Swift记忆游戏卡片创建数量异常
我在使用Swift开发记忆类游戏,用户可以在菜单视图控制器选择游戏界面展示的卡片总数量,选择完成后该参数会被传递到游戏视图控制器。我通过for循环创建对应卡片的UIButton,每创建一个按钮就存入数组统一管理,但初始化游戏时会出现卡片显示数量不对的问题:选择8张卡片时能正常显示8个,选10张时只会显示9个。
相关代码
func createCardButtons(){ let initialPos = Int(screenWidth/50) var xPos = Int(screenWidth/20) var yPos = Int(screenHeight/20) let size = Int(screenWidth/5) let cardBack = #imageLiteral(resourceName: "card_back") for _ in 0...cardsNum! { if xPos < Int(screenWidth - size) { let button = UIButton(frame: CGRect(x: xPos, y: yPos, width: size, height: size)) button.addTarget(self, action: #selector(cardPressed), for: .touchUpInside) button.setImage(cardBack, for: .normal) view.addSubview(button) buttonsArr.append(button) xPos += size + initialPos } else { xPos = Int(screenWidth/20) yPos += size + initialPos } } print(buttonsArr) }
控制台打印信息
- 选择8张卡片时的打印结果:
[<UIButton: 0x157509950; frame = (18 40; 75 75); opaque = NO; layer = <CALayer: 0x6000029dba60>>, <UIButton: 0x157509c10; frame = (100 40; 75 75); opaque = NO; layer = <CALayer: 0x6000029dae60>>, <UIButton: 0x157509ed0; frame = (182 40; 75 75); opaque = NO; layer = <CALayer: 0x6000029db960>>, <UIButton: 0x15750a190; frame = (264 40; 75 75); opaque = NO; layer = <CALayer: 0x6000029dacc0>>, <UIButton: 0x15750a450; frame = (18 122; 75 75); opaque = NO; layer = <CALayer: 0x6000029dbc20>>, <UIButton: 0x15750a710; frame = (100 122; 75 75); opaque = NO; layer = <CALayer: 0x6000029daca0>>, <UIButton: 0x15750a9d0; frame = (182 122; 75 75); opaque = NO; layer = <CALayer: 0x6000029dbb00>>, <UIButton: 0x15750ac90; frame = (264 122; 75 75); opaque = NO; layer = <CALayer: 0x6000029dbcc0>>]
- 选择10张卡片时的打印结果:
[<UIButton: 0x15851ba30; frame = (18 40; 75 75); opaque = NO; layer = <CALayer: 0x600002057bc0>>, <UIButton: 0x15851bcf0; frame = (100 40; 75 75); opaque = NO; layer = <CALayer: 0x600002043300>>, <UIButton: 0x15851bfb0; frame = (182 40; 75 75); opaque = NO; layer = <CALayer: 0x60000204fba0>>, <UIButton: 0x15851c270; frame = (264 40; 75 75); opaque = NO; layer = <CALayer: 0x60000204a340>>, <UIButton: 0x15851c530; frame = (18 122; 75 75); opaque = NO; layer = <CALayer: 0x60000204a1a0>>, <UIButton: 0x15851c7f0; frame = (100 122; 75 75); opaque = NO; layer = <CALayer: 0x60000206be80>>, <UIButton: 0x15851cab0; frame = (182 122; 75 75); opaque = NO; layer = <CALayer: 0x600002061840>>, <UIButton: 0x15851cd70; frame = (264 122; 75 75); opaque = NO; layer = <CALayer: 0x600002063a40>>, <UIButton: 0x15851d030; frame = (18 204; 75 75); opaque = NO; layer = <CALayer: 0x600002063420>>]
问题原因
两个逻辑错误叠加,才会出现8张正常、10张少一个的现象:
- 循环区间写错
Swift中0...cardsNum!是闭区间,会从0遍历到cardsNum,总执行次数是cardsNum + 1次,不是需要的cardsNum次。比如cardsNum=8时,循环实际跑9次。 - 换行逻辑空跑
当x坐标超出当前行可容纳范围、进入else分支时,代码只重置了x坐标、累加了y坐标,没有在新行的这个位置创建按钮,这一次循环没有生成任何按钮,也不会往数组里加元素。
两个问题叠加后的实际效果:
cardsNum=8时,循环跑9次,其中1次刚好走到换行的else分支空跑,剩下8次正常创建按钮,刚好凑够8个,看起来"正常"。cardsNum=10时,循环跑11次,其中2次走到换行的else分支空跑,剩下9次创建按钮,最终数组里只有9个元素,少1张卡片。
修复方法
- 把循环区间改成半开区间
0..<cardsNum!,保证循环刚好执行cardsNum次。 - 换行重置坐标后,直接在新坐标位置创建按钮,不要让循环轮次空耗。
修复后的核心循环代码:
// 半开区间保证循环次数和卡片数一致 for _ in 0..<cardsNum! { if xPos < Int(screenWidth - size) { let button = UIButton(frame: CGRect(x: xPos, y: yPos, width: size, height: size)) button.addTarget(self, action: #selector(cardPressed), for: .touchUpInside) button.setImage(cardBack, for: .normal) view.addSubview(button) buttonsArr.append(button) xPos += size + initialPos } else { // 换行重置坐标后直接创建按钮 xPos = Int(screenWidth/20) yPos += size + initialPos let button = UIButton(frame: CGRect(x: xPos, y: yPos, width: size, height: size)) button.addTarget(self, action: #selector(cardPressed), for: .touchUpInside) button.setImage(cardBack, for: .normal) view.addSubview(button) buttonsArr.append(button) xPos += size + initialPos } }
如果觉得重复写创建按钮的代码太冗余,可以把创建按钮的逻辑抽成单独的方法,传入x、y坐标返回生成好的按钮,减少重复代码。
内容的提问来源于stack exchange,提问作者Matheus Farias
相关产品推荐
相关产品推荐

