make 3 random elements in the array invisible - ios

I have 5 buttons in an array I want to make random three of these invisible when pressed on button. so the last two buttons will be visible
I tried like this:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
let buttons:[UIButton] = [buttonA,buttonB,buttonC,buttonD,buttonE]
let randomNumber = Int(arc4random_uniform(UInt32(3)))
buttons[randomNumber].isHidden = !buttons[randomNumber].isHidden
}
but it takes first elements [0,1,2] and only 1 button is invisible at each press

You are currently only selecting one button. Change that to a loop to select 3. Remove the selected button from the array and make it invisible. Finally, make the remaining buttons visible:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
var buttons:[UIButton] = [buttonA,buttonB,buttonC,buttonD,buttonE]
// Select 3 buttons randomly and hide them
for _ in 1...3 {
let randomNumber = Int(arc4random_uniform(UInt32(buttons.count)))
let button = buttons.remove(at: randomNumber)
button.isHidden = true
}
// Make the remaining buttons visible
for button in buttons {
button.isHidden = false
}
}

I've just written a neat extension to get a random elements from an array:
extension Array {
func randomItems(count: Int) -> [Element] {
guard count <= self.count else { fatalError() }
var notUsedElements: [Element] = self
var randomElements: [Element] = []
while randomElements.count != count {
randomElements.append(notUsedElements.remove(at: Int(arc4random_uniform(UInt32(notUsedElements.count)))))
}
return randomElements
}
}
Using this you can achieve what you want this way:
#IBAction func eliminateChoiceClicked(_ sender: Any) {
let buttons:[UIButton] = [buttonA, buttonB, buttonC, buttonD, buttonE]
// make sure all are visible at the beginning
buttons.forEach { (button) in
button.isHidden = false
}
// hide random 3:
buttons.randomItems(count: 3).forEach { (button) in
button.isHidden = true
}
}

Related

Add LongPressGesture to many textfields

I have many textfields to enter values for calculation.
For each textfield I also added a LongPressGestureRecognizer so that I can update my calculations with interim results that I store in the placeholders.
#IBAction func lTap1(_ sender: UILongPressGestureRecognizer) {
if sender.state == .began && lTap1.placeholder!.isNumeric
{
lTap1.text = lTap1.placeholder
Calculation()
}
}
Is there a more convenient way with less code to add the long tap function to each text field instead of repeating the #IBAction function for lTap2, lTap3, etc.?
First assign tag to all Textfields
then declare the array of TextFields there are two ways to declare
#IBOutlet var textFields: [UITextField]!
and
let textFields = [lTap1, lTap2, lTap3,...]
then
#objc func textFeildLongPressed(_ sender: UILongPressGestureRecognizer) {
guard let tag = sender.view?.tag else { return }
guard let textField = textFields[tag] else {
return
}
if sender.state == .began && textField.placeholder!.isNumeric {
textField.text = textField.placeholder
Calculation()
}
}
Now assign gesture to all textFields in viewDidLoad function
textFields.forEach {
let tap = UILongPressGestureRecognizer(target: self, action: #selector(textFeildLongPressed(_:)))
tap.view?.tag = $0.tag
$0.isUserInteractionEnabled = true
$0.addGestureRecognizer(tap)
}
Thanks.

how to iterate through functions with button press in Swift

I'm learning swift and building an ARApp but can't seem to understand how to iterate through a few functions with every separate button press. I have created 3 func with animation built in and want to press the button once and funcAnimation#1 activate then tap button again to proceed to funcAnimation#2 and so forth.
#IBAction func nextAnimation(_ sender: UIButton) {
funcAnimation#1()
funcAnimation#2()
funcAnimation#3()
}
of course the problem here is that they all activate at once. I would like to iterate only on button press for each individual press. In addition I would also like to have a backButton that reverses the current animation to previous animation. I read in Apple's documentation that there is a addTarget method but I don't understand how it works or how to implement it. Please Help!
Your code should like this :
// You can define this variable globally...
var counter = 0
#IBAction func nextAnimation(_ sender: UIButton) {
if (counter == 0) {
funcAnimation1()
// Increase counter count by 1 and you can add this line to completion of animation.
// You can also disable your button interaction until your animation gets complete and that way you can handle your UI
count += 1
}
else if (counter == 1) {
funcAnimation2()
// Increase counter count by 1 and you can add this line to completion of animation.
count += 1
}
else if (counter == 2) {
funcAnimation3()
// set your counter to 0 again to loop through your animation.
counter = 0
}
}
Your Back Action should look like this:
#IBAction func backAnimation(_ sender: UIButton) {
if (counter == 0) {
funcAnimation1()
// set your counter to 2 again to loop through your animation.
count = 2
}
else if (counter == 1) {
funcAnimation2()
// decrease counter count by 1 and you can add this line to completion of animation.
// You can also disable your button interaction until your animation gets complete and that way you can handle your UI
count -= 1
}
else if (counter == 2) {
funcAnimation3()
// decrease counter count by 1 and you can add this line to completion of animation.
count -= 1
}
}
Another way!
Just set tag of button 1 (Ex. btnObj.tag=1)
And manage method(s) in action of the button like
My suggestion is also manage flag for animation like if 1st animation is in process then 2nd will be waiting for finish and after finishing 1st animation, button will be clicked so your animation will not mismatch.
var isAnimated = false;
#IBAction func myAnimationMethod(_ sender: UIButton) {
if isAnimated {return} // Or display appropriate message by alert
if (sender.tag == 1) {
isAnimated=true
funcAnimation1(...Your Code... After finish to set isAnimated=false)
sender.tag = 2
}
else if (sender.tag == 2) {
isAnimated=true
funcAnimation2(...Your Code... After finish to set isAnimated=false)
sender.tag = 3
}
else if (sender.tag == 3) {
isAnimated=true
funcAnimation3(...Your Code... After finish to set isAnimated=false)
sender.tag = 1
}
}
You can forward your animations and go back to previous animation state through a back button like this .
Step 1 : Declare two integer type variables
var tapCount = 0 //For forwarding your animations from first to third
var currentAnimation = 0 // For reversing the animation from current animation
Step 2 : In your IBAction Function
#IBAction func nextAnimation(_ sender: Any)
{
if tapCount == 0
{
if currentAnimation == 1
{
Animation2()
}
else
{
Animation1()
}
tapCount += 1
}
else if tapCount == 1
{
if currentAnimation == 2
{
Animation3()
}
else
{
Animation2()
}
tapCount += 1
}
else if tapCount == 2
{
if currentAnimation == 3
{
Animation1()
}
else
{
Animation3()
}
tapCount = 0
}
}
Step 3 : In your functions
func Animation1() {
currentAnimation = 1
print("First Animation")
}
func Animation2() {
currentAnimation = 2
print("Second Animation")
}
func Animation3() {
currentAnimation = 3
print("third Animation")
}
Step 4: Lastly For Reversing the animation from current state
#IBAction func backAnimation(_ sender: Any)
{
if currentAnimation == 2
{
Animation1()
}
else if currentAnimation == 3
{
Animation2()
}
else
{
}
tapCount = 0
}
Hope it helps !!
For addTarget(_:action:for:) method , this can be done without connecting a IBAction and declaring it in viewWillAppear or viewDidLoad as required.It will Associates a target object and action method with the control.
For example :
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
nextButton.addTarget(self, action: #selector(self. nextAnimation), for: .touchUpInside) //Declared outlet as nextButton
backButton.addTarget(self, action: #selector(self. backAnimation), for: .touchUpInside) //Declared outlet as backButton and this adds a target
}
#objc func nextAnimation(sender: UIButton) {
// Do your stuff for next animation
}
#objc func backAnimation(sender: UIButton) {
// Do your stuff for previous animation
}

Looping programmed labels and buttons

In the last viewcontroller of my app I wrote a code which creates a number of labels which are filled with random characters from a set
Under the labels textfields appear where the user should match the characters from the labels. When he hits the button, his answer is checked. If the answer is right the score is updated.
After that the score label should update, and new text for the labels with characters should be generated and shown, but that's where I get stuck... My instinct would be to add a loop to the code in the viewDidLoad, but as the code to check the answer is in the buttonAction function outside of the viewDidLoad, I don't know how to do this...
Here is some code from my app which might clarify things. I deleted quite a lot of straightforward code and put a comment line in just for this example.
override func viewDidLoad()
{
super.viewDidLoad()
// variables are declared, the number of labels is calculated based on the frame size
// the text for the labels is calculated based on a function in a separate class I wrote
var a = 0
while a < numberOfLabels {
// the UILabels are made and filled
// the corresponding UITextFields are made
a += 1
labelX += labelWidth
}
// then here the button is coded, the last lines are:
myButton.addTarget(self, action: #selector(buttonAction), for: .touchUpInside)
view.addSubview(myButton)
}
#objc func buttonAction(sender: UIButton!) {
var a = 0
var rightAnswer = false
var userInput: [String] = Array()
while a < numberOfLabels.shared.category! {
if let theLabel = self.view.viewWithTag(a) as? UITextField {
let tekstInput = theLabel.text
userInput.insert(tekstInput!, at:a-1)
}
a = a + 1
}
let controle = Controle()
rightAnswer = controle.checkAnswer(userAnswer: userInput)
if rightAnswer {
if var score = PassScore.shared.category {
score += 1
PassScore.shared.category = score
}
else {
var score = 1
PassScore.shared.category = score
}
}
return
}
Have your initialization code in a function
func setupLabels() {
var a = 0
while a < numberOfLabels {
// the UILabels are made and filled
// the corresponding UITextFields are made
a += 1
labelX += labelWidth
}
}
And call it whenever you need it:
override func viewDidLoad()
{
super.viewDidLoad()
setupLabels()
}
#objc func buttonAction(sender: UIButton!) {
//...
setupLabels()
}

How to know the sender's identifier in Swift

I have two UILabels with two UITapGestureRecognizers in a UITableViewCell.
cell.Username.tag = indexPath.row
cell.SharedUser.tag = indexPath.row
let tapGestureRecognizer2 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
let tapGestureRecognizer3 = UITapGestureRecognizer(target:self, action:"GoToProfil:")
cell.Username.userInteractionEnabled = true
cell.Username.addGestureRecognizer(tapGestureRecognizer2)
cell.SharedUser.userInteractionEnabled = true
cell.SharedUser.addGestureRecognizer(tapGestureRecognizer3)
func GoToProfil (sender: AnyObject!) {
self.performSegueWithIdentifier("GoToProfilSegue", sender: sender)
}
I'm using a Segue to push another UIViewController, and I'm overriding the PrepareSegue function to send the needed information corresponding to the Sender tag.
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
let ProfilView = segue.destinationViewController as! Profil
ProfilView.hidesBottomBarWhenPushed = true
ProfilView.title = posts[sender.view!.tag].User?.objectForKey("Name") as? String
ProfilView.User = posts[sender.view!.tag].User
}
My problem is that I want to know which UILabel was pressed, knowing that I'm already using tag.
Your GoToProfile: function should be written properly. The parameter isn't the "sender", it's the gesture recognizer.
func GoToProfil (gestureRecognizer: UITapGestureRecognizer) {
}
From there, you can determine the label by using the view property of the gesture recognizer.
But you seem to have two conflicting requirements. You want to know which of the two labels was tapped and you want to know which row the label is in.
Normally you would use the label's tag to know which of the two labels was tapped. But you are using their tags to track the row.
The solution I recommend is to use the tag to differentiate the two labels. Then you can calculate the row based on the frame of the label.
See the following answer for sample code that translates the frame of a cell's subview to the cell's indexPath.
Making the following assumptions:
You are trying to uniquely identify the label using UIView.tag
You want different behaviour for Username & SharedUser
I recommend the following, first define your tags below your #imports
#define kUsername 1
#define kSharedUser 2
Then assign them to your views
cell.Username.tag = kUsername
cell.SharedUser.tag = kSharedUser
Then in your prepareSegue
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
int tag = [sender.view!.tag]
if (tag == kUsername) {
//Username logic
} else if(tag == kSharedUser) {
//Shared User Logic
}
}
This way you can easily and simply determine tap, Note this might have different results if you have more then 1 Username & SharedUser labels. Then you will either need more #defines or change how you generate your tags.
You can add a property to UILabel to track the label's type. (I used an enum since there's just 2 cases, but it could be a string, etc.)
enum LabelDest : String
{
case Username = "Username"
case SharedUser = "SharedUser"
}
extension UILabel
{
struct Static {
static var key = "labelDest"
}
var labelDest:LabelDest? {
set { objc_setAssociatedObject( self, &Static.key, newValue?.rawValue, .OBJC_ASSOCIATION_COPY_NONATOMIC )
}
get {
guard let val = objc_getAssociatedObject( self, &Static.key ) as? String else { return nil }
return LabelDest( rawValue:val )
}
}
}
Now you can just do this:
let label = UILabel()
label.labelDest = .Username
Later:
switch label.labelDest
{
case .Some(.Username):
// handle user name
break
...
If you want to use the .tag field on your labels you can use a different technique to find the table row associated with a label: (again using class extensions)
extension UIView
{
var enclosingTableViewCell:UITableViewCell? {
return superview?.enclosingTableViewCell
}
var enclosingTableView:UITableView? {
return superview?.enclosingTableView
}
}
extension UITableViewCell
{
var enclosingTableViewCell:UITableViewCell? {
return self
}
}
extension UITableView
{
var enclosingTableView:UITableView? {
return self
}
}
extension UIView {
var tableRow:Int? {
guard let cell = self.enclosingTableViewCell else { return nil }
return self.enclosingTableView?.indexPathForCell( cell )?.row
}
}
Now, from your gesture recognizer action:
func goToProfil( sender:UIGestureRecognizer! )
{
guard let tappedRow = sender.view?.tableRow else { return }
// handle tap here...
}
You can access the sender data, and read the tag of the object that send you, like in this sample code.
To uniquely identify each row and each label, you can use something like this:
cell.Username.tag = (indexPath.row*2)
cell.SharedUser.tag = (indexPath.row*2)+1
With this, if you have a even tag, its the Username, odd will be the SharedUser. Dividing by the floor of 2 you can have the row back.
#IBOutlet weak var test1: UILabel!
#IBOutlet weak var test2: UILabel!
override func viewWillAppear(animated: Bool) {
test1.tag = 1
test2.tag = 2
test1.userInteractionEnabled = true
test2.userInteractionEnabled = true
self.test1.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
self.test2.addGestureRecognizer(UITapGestureRecognizer(target: self, action: "handleSingleTap:"))
}
func handleSingleTap(sender: UITapGestureRecognizer) {
print(sender.view?.tag)
}

How do I disable all buttons when one gets pressed in XCode?

How do I make it so that when I press one button in XCode, the rest of the buttons (including the one that was pressed) become disabled? Of course I still want the function to be carried out by the button that gets pressed. I just don't want the users to be able to press any button more than once, nor do I want them to be able to press another button after they've already pressed a first one. Below are my IBActions for my two buttons in this case:
#IBAction func addVote1(sender: AnyObject) {
var query = PFQuery(className: "VoteCount")
query.getObjectInBackgroundWithId("BiEM17uUYT") {
(voteCount1: PFObject!, error: NSError!) ->Void in
if error != nil {
NSLog("%#", error)
} else {
voteCount1.incrementKey("votes")
voteCount1.saveInBackgroundWithTarget(nil, selector: nil)
}
let votes = voteCount1["votes"] as Int
let votes2 = voteCount1["votes2"] as Int
self.pollResults1.text = "\(votes) votes \(votes2) votes"
}
}
#IBAction func addVote2(sender: AnyObject) {
var query = PFQuery(className: "VoteCount")
query.getObjectInBackgroundWithId("BiEM17uUYT") {
(voteCount1: PFObject!, error: NSError!) -> Void in
if error != nil {
NSLog("%#", error)
} else {
voteCount1.incrementKey("votes2")
voteCount1.saveInBackgroundWithTarget(nil, selector: nil)
}
let votes = voteCount1["votes"] as Int
let votes2 = voteCount1["votes2"] as Int
self.pollResults2.text = "\(votes) votes \(votes2) votes"
}
}
}
Set up #IBOutlet properties for the buttons if you haven't already, then add a lazy var array of the buttons. In the button handler, set each button's enabled property to false.
class ViewController {
#IBOutlet var button1: UIButton!
#IBOutlet var button2: UIButton!
lazy var buttons: [UIButton] = [self.button1, self.button2]
// ...
#IBAction func addVote1(sender: AnyObject) {
for button in self.buttons {
button.enabled = false
}
// ...
}
}
You can loop through all subview in UIView and find if is a UIButton, if is you can disable the button.
func disableButtons() {
for views in view.subviews {
if let button = views as? UIButton {
button.enabled = false
}
}
}
Do one thing, give unique Tag to all buttons. After that create the method which creates button and disable them by using button tags
func disableButton()
{
for tagvalue in 101...102
{
var btnTemp = self.view.viewWithTag(tagvalue) as UIButton;
btnTemp.enabled = false;
}
}
Add above method in your button, as shown in below code
#IBAction func addVote1(sender: AnyObject)
{
//Your code
disableButton()
}
#IBAction func addVote2(sender: AnyObject)
{
//Your code
disableButton()
}
The easiest way is to add a UIView with a background color of UIColor.clearColor() on top. It's invisible and captures all taps.
class ViewController {
private var uiBlocker = UIView()
override func viewDidLoad() {
uiBlocker.backgroundColor = UIColor.clearColor()
}
#IBAction func buttonAction() {
view.addSubView(uiBlocker)
[stuff you want to do]
uiBlocker.removeFromSuperView()
}
}
let subviews : NSArray = headerView.subviews as NSArray
for button in subviews {
if let button = button as? UIButton {
//let btn = button as! UIButton
button.isSelected = false
}
}
sender.isSelected = true

Resources