How to loop Multiple UIButton in swift - ios

I intended to make new position of my UIButton Frame by using loop but I got no luck, here is my code:
override func viewDidLoad() {
super.viewDidLoad()
for (var i = 75 ; i <= 275; ++i) {
let marginLoopButton: CGFloat = 175.0
let widthLoopButton = view.bounds.width - 2 * marginLoopButton
let marginYLoopButton: CGFloat = 295.0
let minimalYLoopButton = view.bounds.maxY - 2 * marginYLoopButton
self.loopButtonTwo.frame = CGRect(x: i, y: minimalYLoopButton, width: widthLoopButton, height: 70)
self.loopButtonTwo.setTitle("Orange", forState: UIControlState.Normal)
self.view.addSubview(self.loopButtonTwo)
}
}
so the first button position will be different than the second button. The problem is : self.loopButtonTwo.frame = CGRect(x: i, y: minimalYLoopButton, width: widthLoopButton, height: 70) When I put the i on x: Xcode gives me error message : "Cannot invoke initializer for type CGRect with an argument list of type (x: int, y:CGFloat, width"
and also how to multiply the number in swift? I know in C we just write for (i = 75 ; i <= 275; +=10){} but how to write in swift?
Anyone can help me?

Just cast i to CGFloat.
self.loopButtonTwo.frame = CGRect(x: CGFloat(i), y: minimalYLoopButton, width: widthLoopButton, height: 70)

Related

Offset 2D-Matrix: equal spacing in pattern

Let's first take a look at the Screenshot:
My problem:
I just cannot figure out how to change the spacing between the horizontal lines. This spacing is a little bit too wide, as you can see compared to the vertical lines.
This is how I implemented the pattern of UIButtons:
func buildBoard(){
for i in 0...8{
for j in 0...8{
rowBtnArr.append(setupBtn(x: i, y: j))
}
}
}
func setupBtn( x: Int, y: Int)->UIButton{
let btnSize = Int(UIScreen.main.bounds.width / 10) //40
let buffer = 1
if(y%2==0){ //even lines: normal
button = UIButton(frame: CGRect(x: x*btnSize, y: y*btnSize, width: btnSize-buffer, height: btnSize-buffer))
}else{ //odd lines: shift by 1/2 button width
button = UIButton(frame: CGRect(x: x*btnSize+(btnSize/2), y: y*btnSize, width: btnSize-buffer, height: btnSize-buffer))
}
button.layer.cornerRadius = 0.5 * button.bounds.size.width
//unimportant color and target lines are left out
return button
}
Thanks for any help!
SwiftHobby
I think one thing you have to consider is that because you are offsetting the rows every even row, there is more space vertically because the peak height of your circles matches up with the intersection of circles above it. So what I would do is subtract some number to the btnSize before you multiply it by y. This should tighten the y-spacing.
func setupBtn( x: Int, y: Int)->UIButton{
let btnSize = Int(UIScreen.main.bounds.width / 10) //40
let buffer = 1
if(y%2==0){ //even lines: normal
button = UIButton(frame: CGRect(x: x*btnSize, y: y*(btnSize-1), width: btnSize-buffer, height: btnSize-buffer))
}else{ //odd lines: shift by 1/2 button width
button = UIButton(frame: CGRect(x: x*btnSize+(btnSize/2), y: y*(btnSize-1), width: btnSize-buffer, height: btnSize-buffer))
}
button.layer.cornerRadius = 0.5 * button.bounds.size.width
//unimportant color and target lines are left out
return button
}
This way the y-spacing is closer than the x-spacing, accounting for the mismatch I was talking about earlier. You need to experiment with the y-spacing to get the visual difference you want, so my correction above might be weird looking as well.
maybe you want this effect:
I use your code and make some changes, and use a Array to cache all buttons's frame like blow:
// user a Array to cache all frames
lazy var framesMatrix: [[CGRect]] = Array(repeating: Array(repeating: CGRect.zero, count: 9), count: 9)
func setupBtn( x: Int, y: Int)->UIButton{
let btnCount = 10
let buffer: CGFloat = 1
let btnSize = (UIScreen.main.bounds.width - CGFloat(btnCount - 1) * buffer) / CGFloat(btnCount)
var button: UIButton = UIButton()
if y % 2 == 0 {
if y == 0 {
let frame = CGRect(x: CGFloat(x)*(btnSize+buffer), y: CGFloat(y)*btnSize, width: btnSize, height: btnSize)
framesMatrix[x][y] = frame
button = UIButton(frame: frame)
} else {
let lastFrame = framesMatrix[x][y-1]
// Here is the change
let newCenterY = lastFrame.midY + sqrt(3.0) * 0.5 * (btnSize+buffer);
let frame = CGRect(x: lastFrame.minX - btnSize * 0.5 , y: newCenterY - btnSize * 0.5, width: btnSize, height: btnSize);
framesMatrix[x][y] = frame
button = UIButton(frame: frame)
}
} else {
let lastFrame = framesMatrix[x][y-1]
// Here is the change
let newCenterY = lastFrame.midY + sqrt(3.0) * 0.5 * (btnSize+buffer);
let frame = CGRect(x: lastFrame.midX + buffer * 0.5, y: newCenterY - btnSize * 0.5 , width: btnSize, height: btnSize);
framesMatrix[x][y] = frame
button = UIButton(frame: frame)
}
button.layer.cornerRadius = 0.5 * button.bounds.size.width
button.backgroundColor = UIColor.black
//unimportant color and target lines are left out
return button
}

Add possible input types to CGRect Swift

I want to be able to pass a percentage value to a CGRect
I created a Percentage class to get the input and then created four values (x, y, width, height)
Now I need to create an extension for CGRect to be able to use the Percentage values in it. The extension returns an error.
extension CGRect {
init(x: Percentage, y: Percentage, width: Percentage, height: Percentage) {
self.init(x: Percentage.x, y: Percentage.y, width: Percentage.width, height: Percentage.height)
ERROR: Instance member 'x' cannot be used on type 'Percentage'
}
}
class Percentage {
let x: CGFloat
let y: CGFloat
let width: CGFloat
let height: CGFloat
init(_ value: CGFloat) {
self.x = UIScreen.main.bounds.width * value
self.y = UIScreen.main.bounds.height * value
self.width = UIScreen.main.bounds.width * value
self.height = UIScreen.main.bounds.height * value
}
}
Thanks for your help ;)
EDIT:
I wanna be able to use it like this:
CGRect(x: Percentage(0.5), y: 50, width: 100, height: 100)
Instance member 'x' cannot be used on type 'Percentage'
you're using type of class so even if you had in parameter percentage, it wouldn't work.
But better would be if you just passed certain Percentage as parameter for init and then use its properties (use it with small p because you need init parameter, not your class type)
init(percentage: Percentage) {
self.init(x: percentage.x, y: percentage.y, width: percentage.width, height: percentage.height)
}
After your edit I have to say that you don’t have to use custom init and you can just pass property of your Percentage
CGRect(x: yourPercentage.x, y: 50, ...)
Think there is no need for a custom extension/classes - just use:
let view = UIView(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
let viewRect = view.frame
let scaleFactor:CGFloat = 0.5
let scaledFrame = viewRect.applying(CGAffineTransform(scaleX: scaleFactor, y: scaleFactor))
print("Scaled size: \(scaledFrame)")
// Scaled size: (0.0, 0.0, 50.0, 50.0)
However, you also can move this "feature" to an extension as well.

Where should I write the code to add a label?

I want to add a label. Where should I write the code to add the label?
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
var x = 20.0
var y = 100.0
let width = 100.0
let offsetX = 80.0
let offsetY = 80.0
for i in 1 ... 9 {
let button = UIButton(frame: CGRect(x: x, y: y, width: width, height: width))
button.tag = i
button.setTitle("\(i)", for: UIControlState.normal)
button.setTitleColor(UIColor.green, for: UIControlState.normal)
button.addTarget(self, action: #selector(self.buttonPressed(button:)), for: UIControlEvents.touchUpInside)
self.view.addSubview(button)
x = x + offsetX
if i%3 == 0 {
y = y + offsetY
x = 20.0
}
}
}
#objc func buttonPressed (button:UIButton) {
print("Button Pressed: \(button.tag)")
}
}
let lblNew = UILabel(frame: CGRect(x: x, y: y, width: width, height: width))
lblNew.backgroundColor = UIColor.clear
lblNew.text = "Label Text"
lblNew.textColor = UIColor.black
view.addSubview(lblNew)
If you simply want to add label. You can use the following code.
let lblNew = UILabel(frame: CGRect(x: x, y: y, width: width, height: width))
lblNew.backgroundColor = UIColor.clear
lblNew.text = "Label Text"
lblNew.textColor = UIColor.black
view.addSubview(lblNew)
If you want to add with every button. you can put inside the loop and change the values of x and y accordingly.

Swift Extra argument "width" in call in CGRect

I am trying to add a few buttons in the code
and i get an error "Extra argument "width" in call"
let yPos = txt.frame.origin.y + txt.frame.height
for i in 0..<count {
let yt : Int = i*40 + yPos
//let b = UIButton(frame: CGRect(x: xPos, y: 40, width: 100, height: 30))
var b = UIButton(frame: CGRect(x: xPos, y: yt, width: 100, height: 30))
b.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "test:"))
b.backgroundColor = UIColor.blackColor()
b.tag = i
self.addSubview(b)
}
I cant figure out what is the problem.
I tried to cast all the argument to Int but i get the same error.
Any suggestions?
my mistake i had to cast all the arguments to CGFloat
for i in 0...2 {
let yt : CGFloat = CGFloat(i*40) + yPos
//let b = UIButton(frame: CGRect(x: xPos, y: 40, width: 100, height: 30))
var b = UIButton(frame: CGRect(x: xPos, y: yt, width: 100, height: 30))
b.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "test:"))
b.backgroundColor = UIColor.blackColor()
b.tag = i
self.addSubview(b)
}

IOS swift expandable view [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 1 year ago.
Improve this question
I would like to expand an view when I switch a switch to on, and to collapse when I switch the switch to off.
At the same time all the other elements I have in my design have to move down or up.
Here is how the layout looks like when the switches are OFF
And here is how the layout looks like when the switches are ON:
I hope you understand what I am trying to do.
And the question... How do I do that???
It's much easier to do with AutoLayout and inside InterfaceBuilder (with a xib or storyboard), so foremost familiarize yourself with them if needed.
Wrap a container UIView around the UISwitches and make sure to set it to Clip Subviews.
Add a constraint for the container view's height and set it to 0.
Drag that constraint into your class declaration to generate an IBOutlet property.
Drag a new outlet from your UISwitch 'Value Changed' event to the class to generate an IBAction method.
Make sure to implement that method so that when the value changes, based on the change you toggle-animate the height constraint (the IBOutlet)
self.firstHeightConstraint.constant = on ? 200.0 : 0.0
self.view.setNeedsUpdateConstraints()
UIView.animateWithDuration( ...
options:.BeginFromCurrentState,
animations: { self.view.layoutIfNeeded() } ...
)
Using UIView, it's not a good way. You should use UITableView instead of views.
Using TableView, you can add UISwitch with title according to the first image, in UITableView's header and return required number of rows in changed section in TableView on state ON and return the number of rows 0 on state OFF.
Still an issue, please let me know I'll help you.
I know my code isnt the cleanest, but it bothers me when answers tell the questioner its not a good way or do it this way instead. I am offering a solution NOT using table views or constraints:
In your example, you can use tags for each "level" that needs to be moved. So starting with switch 2 and all views below that, you would have a tag for that level (the second hidden collapsable view would have a tag of 2 as well), then switch 3 would have a tag of 3.
For the labels hidden, you are adding them to a subview i call insideView1 or insideView2. The frames of those inside views should have the proper minX, minY and width, but have a height of 0 to start. Also importantly they have layer.masksToBounds = false, so their subviews dont show when their height is 0. We then animate the height changing to the proper height (expanding), as well as all subviews under changing position.
Lastly i made a dictionary to keep track of when a section is expanded so you can use the same function to expand/collapse other areas while one is already expanded.
import UIKit
class ExpandViewController: UIViewController {
var insideView1 = UIView()
var insideView2 = UIView()
var expand = [1:true,2:true]
#objc func expandCollapse(_ sender : UIButton) {
var heightChange = CGFloat(0)
switch sender.tag {
case 1:
heightChange = 90
default:
heightChange = 90
}
var viewsToExpand = [UIView(),insideView1,insideView2]
UIView.animate(withDuration: 1.0) { [self] in
let expandMe = viewsToExpand[sender.tag]
if expand[sender.tag] == true {
expandMe.frame.size.height = heightChange
for v in view.subviews {
if v.tag > sender.tag {
v.center.y += heightChange
}
}
} else {
expandMe.frame.size.height = 0
for v in view.subviews {
if v.tag > sender.tag {
v.center.y -= heightChange
}
}
}
expand[sender.tag] = !expand[sender.tag]!
}
}
override func viewDidLoad() {
super.viewDidLoad()
let switcher1 = UISwitch()
switcher1.tag = 1
switcher1.isOn = false
switcher1.frame = CGRect(x: view.bounds.width - 150, y: 100, width: 40, height: 30)
switcher1.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher1)
let switchLabel1 = UILabel()
switchLabel1.tag = 1
switchLabel1.text = "Switch 1"
switchLabel1.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel1.center.y = switcher1.center.y
view.addSubview(switchLabel1)
let switcher2 = UISwitch()
switcher2.tag = 2
switcher2.isOn = false
switcher2.frame = CGRect(x: view.bounds.width - 150, y: 140, width: 40, height: 30)
switcher2.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher2)
let switchLabel2 = UILabel()
switchLabel2.tag = 2
switchLabel2.text = "Switch 2"
switchLabel2.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel2.center.y = switcher2.center.y
view.addSubview(switchLabel2)
let switcher3 = UISwitch()
switcher3.tag = 3
switcher3.isOn = false
switcher3.frame = CGRect(x: view.bounds.width - 150, y: 180, width: 40, height: 30)
// switcher3.addTarget(self, action: #selector(expandCollapse(_:)), for: .allTouchEvents)
view.addSubview(switcher3)
let switchLabel3 = UILabel()
switchLabel3.tag = 3
switchLabel3.text = "Switch 3"
switchLabel3.frame = CGRect(x: 30, y: 0, width: view.bounds.width, height: 30)
switchLabel3.center.y = switcher3.center.y
view.addSubview(switchLabel3)
insideView1.frame = CGRect(x: 0, y: switcher1.frame.maxY + 5, width: view.bounds.width, height: 0)
insideView1.layer.backgroundColor = UIColor.lightGray.cgColor
insideView1.layer.masksToBounds = true
insideView1.tag = 1
view.addSubview(insideView1)
insideView2.frame = CGRect(x: 0, y: switcher2.frame.maxY + 5, width: view.bounds.width, height: 0)
insideView2.layer.backgroundColor = UIColor.lightGray.cgColor
insideView2.layer.masksToBounds = true
insideView2.tag = 2
view.addSubview(insideView2)
let insideLabel1 = UILabel()
insideLabel1.text = "Label"
insideLabel1.frame = CGRect(x: 60, y: 10, width: view.bounds.width, height: 30)
insideView1.addSubview(insideLabel1)
let insideTextField1 = UITextField()
insideTextField1.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField1.center.y = insideLabel1.center.y
insideTextField1.layer.backgroundColor = UIColor.white.cgColor
insideView1.addSubview(insideTextField1)
let insideLabel2 = UILabel()
insideLabel2.text = "Label"
insideLabel2.frame = CGRect(x: 60, y: 50, width: view.bounds.width, height: 30)
insideView1.addSubview(insideLabel2)
let insideTextField2 = UITextField()
insideTextField2.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField2.center.y = insideLabel2.center.y
insideTextField2.layer.backgroundColor = UIColor.white.cgColor
insideView1.addSubview(insideTextField2)
let insideLabel3 = UILabel()
insideLabel3.text = "Label"
insideLabel3.frame = CGRect(x: 60, y: 10, width: view.bounds.width, height: 30)
insideView2.addSubview(insideLabel3)
let insideTextField3 = UITextField()
insideTextField3.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField3.center.y = insideLabel3.center.y
insideTextField3.layer.backgroundColor = UIColor.white.cgColor
insideView2.addSubview(insideTextField3)
let insideLabel4 = UILabel()
insideLabel4.text = "Label"
insideLabel4.frame = CGRect(x: 60, y: 50, width: view.bounds.width, height: 30)
insideView2.addSubview(insideLabel4)
let insideTextField4 = UITextField()
insideTextField4.frame = CGRect(x: 200, y: 0, width: view.bounds.width - 240, height: 30)
insideTextField4.center.y = insideLabel4.center.y
insideTextField4.layer.backgroundColor = UIColor.white.cgColor
insideView2.addSubview(insideTextField4)
}
}

Resources