How can I make empty circle on baseball out count? - ios

I am making baseball outcount code based on FoodTracker source code. Here's my code but empty circle doesn't count in this code. How can I make the empty circle rating? I would appreciate if you guys can help me out. Thank you!!
///////
import UIKit
class RatingControl: UIView {
// MARK: Properties
var rating = 0 {
didSet {
setNeedsLayout()
}
}
var ratingButtons = [UIButton]()
var spacing = 3
var circles = 3
// MARK: Initialization
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
let filledCircleImage = UIImage(named: "checkedRed")
let emptyCircleImage = UIImage(named: "UncheckedRed")
for _ in 0..<3 {
let button = UIButton()
button.setImage(emptyCircleImage, forState: .Normal)
button.setImage(filledCircleImage, forState: .Selected)
button.setImage(filledCircleImage, forState: [.Highlighted, .Selected])
button.adjustsImageWhenHighlighted = false
button.addTarget(self, action: "ratingButtonTapped:", forControlEvents: .TouchDown)
ratingButtons += [button]
addSubview(button)
}
}
override func layoutSubviews() {
// Set the button's width and height to a square the size of the frame's height.
let buttonSize = Int(frame.size.height)
var buttonFrame = CGRect(x: 0, y: 0, width: buttonSize, height: buttonSize)
// Offset each button's origin by the length of the button plus spacing.
for (index, button) in ratingButtons.enumerate() {
buttonFrame.origin.x = CGFloat(index * (buttonSize + spacing))
button.frame = buttonFrame
}
updateButtonSelectionStates()
}
override func intrinsicContentSize() -> CGSize {
let buttonSize = Int(frame.size.height)
let width = (buttonSize + spacing) * circles
return CGSize(width: width, height: buttonSize)
}
// MARK: Button Action
func ratingButtonTapped(button: UIButton) {
rating = ratingButtons.indexOf(button)! + 1
updateButtonSelectionStates()
}
func updateButtonSelectionStates() {
for (index, button) in ratingButtons.enumerate() {
// If the index of a button is less than the rating, that button should be selected.
button.selected = index < rating
}
}
}

if you want zero to count then return 0 as a possible button action. Your rating is already set to zero so technically it does count. If you accidentally tap the first circle and want to undo it you can try something like this in ratingButtonTapped
//rating = ratingButtons.index(of: button)! + 1
rating = (rating == 1) && (ratingButtons.index(of: button) == 0) ? 0 : (ratingButtons.index(of: button)! + 1)

Related

Reposition other UIButton after delete one

I have a code where the user can input several ingredients and can also delete them.
Method: input ingredient in searchBar then create UIButton for that input and add into UIView. I also have a function to arrange position of all buttons properly
class SearchViewController: UIViewController {
var listInputView = UIView()
let spacingX: CGFloat = 10
let spacingY: CGFloat = 10
let btnHeight: CGFloat = 30
var heightConstraint: NSLayoutConstraint = NSLayoutConstraint()
var listBtn = [UIButton]()
//and also initialization of other components like UISearchBar,...
override func viewDidLoad() {
super.viewDidLoad()
setupSegmentControl()
setupView()
listInputView.translatesAutoresizingMaskIntoConstraints = false
heightConstraint = listInputView.heightAnchor.constraint(equalToConstant: 10)
}
func setupSegmentControl() {
//setup UISegmentedControl
}
fileprivate func setupView() {
//setup UISearchBar
}
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
if segmentControl.selectedSegmentIndex == 0 {
//search from input menu
}
else {
//search from input ingredients
inputSearchBar.append(mainSearchBar.text!)
listIngredients()
}
}
func listIngredients() {
view.addSubview(listInputView)
listInputView.isHidden = false
NSLayoutConstraint.activate([
listInputView.topAnchor.constraint(equalTo: mainSearchBar.bottomAnchor, constant: 10),
listInputView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
listInputView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
heightConstraint
])
createIngredientBtn(ingredient: mainSearchBar.text!)
}
// create a button
func createIngredientBtn(ingredient: String) {
let deleteIcon = UIImage(systemName: "multiply")
let btn = UIButton(type: UIButton.ButtonType.custom) as UIButton
btn.setTitle(ingredient, for: UIControl.State.normal)
btn.setTitleColor(UIColor(hexString: "#707070"),for: UIControl.State.normal)
btn.layer.borderColor = UIColor(hexString: "#707070").cgColor
btn.layer.borderWidth = 1.0
btn.frame.size.height = btnHeight
btn.layer.masksToBounds = true
btn.contentEdgeInsets = UIEdgeInsets(top: 0, left: 8, bottom: 0, right: 8)
btn.setImage(deleteIcon, for: .normal)
btn.imageView?.contentMode = .scaleAspectFit
btn.imageEdgeInsets = UIEdgeInsets(top: 0, left: 10, bottom: 0, right: 3)
btn.semanticContentAttribute = UIApplication.shared.userInterfaceLayoutDirection == .rightToLeft ? .forceLeftToRight : .forceRightToLeft
btn.addTarget(self, action: #selector(deleteSelectedInput), for: UIControl.Event.touchDown)
btn.translatesAutoresizingMaskIntoConstraints = false
listBtn.append(btn)
listInputView.addSubview(btn)
}
// delete a button
#objc func deleteSelectedInput(_ sender: UIButton) {
sender.removeFromSuperview()
listBtn = listBtn.filter {$0 != sender}
showListBtn() //I try to call this here to reposition other buttons
viewDidLayoutSubviews()
inputSearchBar = inputSearchBar.filter {$0 != sender.currentTitle}
if inputSearchBar.isEmpty {
listInputView.isHidden = true
}
}
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
showListBtn()
}
//Arrange position of each button and update height of inputView according to them
func showListBtn() {
let listInputViewWidth = listInputView.frame.size.width
var currentOriginX: CGFloat = 0
var currentOriginY: CGFloat = 0
listBtn.forEach { btn in
// break to newline
if currentOriginX + btn.frame.width > listInputViewWidth {
currentOriginX = 0
currentOriginY += btnHeight + spacingY
}
// set the btn frame origin
btn.frame.origin.x = currentOriginX
btn.frame.origin.y = currentOriginY
// increment current X
currentOriginX += btn.frame.width + spacingX
}
// update listInputView height
heightConstraint.constant = currentOriginY + btnHeight
}
}
Problem: Suppose I have > 1 input ingredients (> 1 button) and I delete one of it, the rest will be overlapped on each other at the leftmost origin (where 1st button is placed). At this moment, the position of them is actually correct, they just don't re-display as they should be.
However, when I try to input new ingredient, they will then display properly.
Here is the image to clarify this better:
Input 3 ingredients: Ing1, Ing2, Ing3
Delete Ing1. Then Ing2 and Ing3 are overlapped at the position of Ing1.
Add new input Ing4. They are all position correctly again.
Why the buttons are not re-position correctly when I delete one of them and how can I solve this ? Please kindly help me. Thank you.
EDIT: I'm able to call viewDidLayoutSubviews after delete but the buttons still not re-draw at new position although the frame.origin has changed correctly.
#objc func deleteSelectedInput(_ sender: UIButton) {
sender.removeFromSuperview()
listBtn = listBtn.filter {$0 != sender}
listInputView.removeAllSubviews()
listBtn.forEach { btn in
listInputView.addSubview(btn)
}
inputSearchBar = inputSearchBar.filter {$0 != sender.currentTitle}
if inputSearchBar.isEmpty {
listInputView.isHidden = true
}
}

Creating a haptic-touch-like button in Swift/Obj-C

I've been playing around with an idea for a button which, when held, expands to reveal other buttons (like the FAB in Android). Without releasing, sliding one's finger down should highlight other buttons, similar to haptic touch's behaviour.
Here's a crude mockup I've created using Drama: https://youtu.be/Iam8Gjv3gqM.
There should also be an option to have a label horizontally next to each button, and the order of buttons should be as shown (with the selected button at the top instead of its usual position).
I already have a class for each button (seen below), but do not know how to achieve this layout/behaviour.
class iDUButton: UIButton {
init(image: UIImage? = nil) {
super.init(frame: CGRect(x: 0, y: 0, width: 30, height: 30))
if let image = image {
setImage(image, for: .normal)
}
backgroundColor = .secondarySystemFill
layer.cornerRadius = 15
}
required init?(coder: NSCoder) {
super.init(coder: coder)
}
}
class iDUBadgeButton: iDUButton {
var badgeLabel = UILabel()
var badgeCount = 0
override init(image: UIImage? = nil) {
super.init(image: image)
setup()
}
required init?(coder: NSCoder) {
super.init(coder: coder)
setup()
}
private func setup() {
badgeLabel.textColor = .white
badgeLabel.backgroundColor = .systemRed
badgeLabel.textAlignment = .center
badgeLabel.font = .preferredFont(forTextStyle: .caption1)
badgeLabel.alpha = 0
updateBadge()
addSubview(badgeLabel)
}
override func layoutSubviews() {
super.layoutSubviews()
badgeLabel.sizeToFit()
let height = max(18, badgeLabel.frame.height + 5.0)
let width = max(height, badgeLabel.frame.width + 10.0)
badgeLabel.frame = CGRect(x: frame.width - 5, y: -badgeLabel.frame.height / 2, width: width, height: height);
badgeLabel.layer.cornerRadius = badgeLabel.frame.height / 2;
badgeLabel.layer.masksToBounds = true;
}
func setBadgeCount(badgeCount: Int) {
self.badgeCount = badgeCount;
self.updateBadge()
}
func updateBadge() {
if badgeCount != 0 {
badgeLabel.text = "\(badgeCount)"
}
UIView.animate(withDuration: 0.25, animations: {
self.badgeLabel.alpha = self.badgeCount == 0 ? 0 : 1
})
layoutSubviews()
}
}
If anyone could help with achieving this layout, I'd be very grateful! Thanks in advance.
PS: I'm developing the UI in Swift, but will be converting it to Objective-C once complete. If you're interested, the use case is to adapt the button in the top-left of this to offer a selection of different tags.

Button with right image aligned x points from the right

I'm trying to create a button that looks like this:
So it has a border and a right imageview.
I subclassed UIButton like this:
class ButtonWithRightImage: UIButton {
override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRectForContentRect(contentRect)
imageFrame.origin.x = CGRectGetMaxX(super.titleRectForContentRect(contentRect)) - CGRectGetWidth(imageFrame)
return imageFrame
}
override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRectForContentRect(contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
}
return titleFrame
}
}
And I created my button as follows:
lazy var testButton: ButtonWithRightImage = {
let button = ButtonWithRightImage(forAutoLayout: ())
button.setTitle("Privacy", forState: .Normal)
button.setTitleColor(UIColor.DarkBlue(), forState: .Normal)
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.grey().CGColor
button.setImage(UIImage(named: "arrow"), forState: .Normal)
button.contentHorizontalAlignment = .Left
button.contentEdgeInsets = UIEdgeInsetsMake(0, 20, 0, 0)
return button
}()
Now my button looks like this:
As you can see the image is not aligned with 20 points from the right.
I can't figure out how to do this. I'm using autolayout to display the button so I can't just modify the frames I guess.
As your ButtonWithRightImage class need modification to achieve output as per your requirement.
class ButtonWithRightImage: UIButton {
override func imageRectForContentRect(contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRectForContentRect(contentRect)
imageFrame.origin.x = CGRectGetWidth(contentRect) - CGRectGetWidth(imageFrame)
return imageFrame
}
override func titleRectForContentRect(contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRectForContentRect(contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = CGRectGetMinX(super.imageRectForContentRect(contentRect))
}
return titleFrame
}
}
Issue is you need to minus image width from container width. Instead of container maxX value.
Let me know if you have any comment on the same.
Thanks
Set imageEdgeInsets for button Like Below code:-
lazy var testButton: ButtonWithRightImage =
{
let button = ButtonWithRightImage(forAutoLayout: ())
button.setTitle("Privacy", forState: .Normal)
button.setTitleColor(UIColor.DarkBlue(), forState: .Normal)
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.grey().CGColor
button.setImage(UIImage(named: "arrow"), forState: .Normal)
button.contentHorizontalAlignment = .Left
button.imageEdgeInsets = UIEdgeInsetsMake(0, 280, 0, 0)
return button
}()
Swift 3 and above solution based on MinuMaster's answer.
Furthermore, provision is added to set right padding for image and left padding for text from storyboard.
Also, have handled the text length so it should not overlap image.
class ButtonWithRightImage: UIButton {
#IBInspectable var imagePaddingRight: CGFloat = 0.0
#IBInspectable var textPaddingLeft: CGFloat = 0.0
override func imageRect(forContentRect contentRect:CGRect) -> CGRect {
var imageFrame = super.imageRect(forContentRect: contentRect)
imageFrame.origin.x = contentRect.width - imageFrame.width - imagePaddingRight
return imageFrame
}
override func titleRect(forContentRect contentRect:CGRect) -> CGRect {
var titleFrame = super.titleRect(forContentRect: contentRect)
if (self.currentImage != nil) {
titleFrame.origin.x = super.imageRect(forContentRect: contentRect).minX + textPaddingLeft
if titleFrame.size.width > frame.size.width - (imageView?.frame.size.width)! - imagePaddingRight {
titleFrame.size.width = titleFrame.size.width - (imageView?.frame.size.width)! - imagePaddingRight
}
}
return titleFrame
}
}

Creating buttons with a for loop? Swift

func makeAButton(){
var tag = 1
for post in postArray {
let cgX = CGFloat(post.x!)
let cgY = CGFloat(post.y!)
let button : DragImg = DragImg(frame: CGRect(x: cgX, y: cgY, width: 100, height: 100))
button.backgroundColor = UIColor.blueColor()
button.setTitle(post.user, forState: .Normal)
button.addTarget(self, action: #selector(self.buttonAction), forControlEvents: .TouchUpInside)
button.tag = tag
self.view.addSubview(button)
print(tag)
tag += 1
}
}
I've currently got 7 things in my post array, and when I print out the tag, I get the
1
2
3
... and so on up to 7.
Whats happening though is that at the 0th item in the aray, 1 button is being created. Then at the 1st, 2 are being created at that x/y, and it keeps going up and up until I'm getting 7 buttons right ontop of eachother.
Why is this the case? I thought here the for loop would say:
For each post in PostArray make a single button using the data from that item in the PostArray.
How might I change this to work as intended?
Edit: (Just posting everything I've got)
import UIKit
import Firebase
class testController: UIViewController {
#IBOutlet weak var button : UIButton!
#IBOutlet weak var textField: UITextField!
var colorsArray = [String]()
var currentX = CGFloat()
var currentY = CGFloat()
var postArray = [CirclePost]()
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let touch = touches.first {
let position :CGPoint = touch.locationInView(view)
currentY = position.y
currentX = position.x
}
}
override func viewDidLoad() {
super.viewDidLoad()
DataService.ds.REF_BASE.child("Colors").observeEventType(.Value) { (snapshot: FIRDataSnapshot) in
if let snapshots = snapshot.children.allObjects as? [FIRDataSnapshot] {
self.postArray = []
for snap in snapshots {
if let x = snap.value!["x"] as? CGFloat, y = snap.value!["y"] as? CGFloat, color = snap.value!["color"] as? String {
let post = CirclePost(postKey: snap.key, user: color , x: x, y: y)
self.postArray.append(post)
print(self.postArray.count)
self.makeAButton()
}
}
}
}
}
func makeAButton(){
var tag = 1
for post in postArray {
let cgX = CGFloat(post.x!)
let cgY = CGFloat(post.y!)
// let button = DragImg()
let button : DragImg = DragImg(frame: CGRect(x: cgX, y: cgY, width: 100, height: 100))
button.backgroundColor = UIColor.blueColor()
button.setTitle(post.user, forState: .Normal)
button.addTarget(self, action: #selector(self.buttonAction), forControlEvents: .TouchUpInside)
button.tag = tag
self.view.addSubview(button)
tag += 1
}
}
func buttonAction(sender: UIButton!) {
let button = postArray[sender.tag - 1]
print(button.user)
print("Button tapped")
}
#IBAction func buttonPressed(sender: AnyObject) {
let ref = DataService.ds.REF_BASE.child("Colors").childByAutoId()
ref.child("color").setValue(textField.text)
ref.child("x").setValue(currentX)
ref.child("y").setValue(currentY)
}
}
Here's your issue:
for snap in snapshots {
...
self.postArray.append(post)
print(self.postArray.count)
self.makeAButton()
You're adding a post to the array, then drawing the array (1 item). Then next time you add an additional post and then draw the array (2 items) and so on.
You need to move the call self.makeAButton() out of that for loop.

How to remove a button when it is clicked?

I'm very new to swift. I've created multiple textfield and button by using subview. The output of my ViewController is below:-
Now I need to remove "-" button and it's corresponding textfield when it is clicked.
But I'm not able to detect which button is being clicked.
This is my code:
var y: CGFloat = 190
var by: CGFloat = 192
#IBAction func addRow(sender: AnyObject) {
y += 30
by += 30
let textFiled = UITextField(frame:CGRectMake(50.0, y, 100.0, 20.0))
textFiled.borderStyle = UITextBorderStyle.Line
let dunamicButton = UIButton(frame:CGRectMake(155.0, by, 15.0, 15.0))
dunamicButton.backgroundColor = UIColor.clearColor()
dunamicButton.layer.cornerRadius = 5
dunamicButton.layer.borderWidth = 1
dunamicButton.layer.borderColor = UIColor.blackColor().CGColor
dunamicButton.backgroundColor = .grayColor()
dunamicButton.setTitle("-", forState: .Normal)
dunamicButton.addTarget(self, action: #selector(removeRow), forControlEvents: .TouchUpInside)
self.view.addSubview(textFiled)
self.view.addSubview(dunamicButton)
}
func removeRow(sender: UIButton!) {
print("Button tapped")
self.view.removeFromSuperview()
}
any help would be appreciated...
eMKA was right! Try this:
import UIKit
class ViewController: UIViewController {
var y:CGFloat = 100
var textFields = [UIButton : UITextField]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
#IBAction func onAddMoreButtonPressed(sender: AnyObject) {
let newButton = UIButton(frame: CGRect(x: 50, y: y, width: 150, height: 20))
newButton.setTitle("New button", forState: .Normal)
newButton.backgroundColor = UIColor.blueColor()
newButton.addTarget(self, action: #selector(ViewController.onNewButtonPressed(_:)), forControlEvents: .TouchUpInside)
self.view.addSubview(newButton)
let newTextField = UITextField(frame: CGRect(x: 200, y: y, width: 150, height: 20))
newTextField.text = "New text field"
self.view.addSubview(newTextField)
textFields[newButton] = newTextField
y += 20
if y > self.view.frame.height {
y = 100
}
}
func onNewButtonPressed(sender: UIButton) {
textFields[sender]?.removeFromSuperview()
sender.removeFromSuperview()
}
}
You can override the method touchesBegan in any view controller. Assuming that you are storing an array of your buttons somewhere
let buttons : [UIButton] = []
you can do the following:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
super.touchesBegan(touches, withEvent: event)
guard let touch: UITouch = touches.first else {
return
}
for button in buttons {
if touch.view == button {
print("This is the button you tapped")
button.removeFromSuperview()
}
}
}

Resources