Swift, Xcode: Using PanGestureRecognizer for quiz app's answer options - ios

Currently I'm building a quiz app with the PanGestureRecognizer functional. The idea is that user can drag one of the answer option to 'questionMark' cell and drop it there. The app should be able to check whether a dropped answer option in the 'questionMark' cell is correct or not.
So here is the question: how can I write a user answerCheck function for this app?
Please see below the code and related screens from my app.
To do that per understanding the following steps should be executed:
1) check whether sender.currentImage intersected 'questionMark' cell (which is UIImageView by nature) or not (if yes, it means that an answer was submitted by user by adding answer option in the 'questionMark' cell).
2) 'correct answr' is the answer which equals to the prescribed 'correct' answer option, however it should be located in 'questionMark' cell. Consequently, app should check whether sender.currentImage equals to the correct answer or not. Of course further actions depends on user answer (correct or wrong).`import UIKit
Obviously my checking function does not work at all. There are even error from Xcode (because of different types 'Bool' and UIImage). So need help, please let me know if my post is not clear and additional comments are required.
class MainViewController: UIViewController {
#IBOutlet weak var questionImageView: UIImageView!
#IBOutlet weak var questionMarkView: UIImageView!
#IBOutlet weak var buttonLabel1: UIButton!
#IBOutlet weak var buttonLabel2: UIButton!
#IBOutlet weak var buttonLabel3: UIButton!
//This var was added to access 'TestBrain' struct and its functions
var testBrain = TestBrain()
override func viewDidLoad() {
super.viewDidLoad()
//GestureRecognizer->>>
buttonLabel1.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture1)))
buttonLabel2.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture2)))
buttonLabel3.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(self.handlePanGesture3)))
}
#IBAction func answerButtonPressed(_ sender: UIButton) {
let userAnswer = sender.currentImage!
let correctAnswer = buttonLabel3.imageView!.frame.intersects(questionMarkView.frame)
if userAnswer == correctAnswer {
print("Correct")enter image description here
} else {
print("Wrong")
}
}
// ObjC functions for GestureRecognizer #1 ->>>
#objc func handlePanGesture1(gesture1: UIPanGestureRecognizer) {
let answerOptionView1 = gesture1.view!
switch gesture1.state {
case .began, .changed:
let translation1 = gesture1.translation(in: self.view)
buttonLabel1.transform = CGAffineTransform(translationX: translation1.x, y: translation1.y)
case .ended:
if answerOptionView1.frame.intersects(questionMarkView.frame) {
answerOptionView1.frame = questionMarkView.frame
answerOptionView1.isUserInteractionEnabled = false
} else {
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1, options: .curveEaseIn, animations: {
self.buttonLabel1.transform = .identity
})
}
default:
break
}
}
// ObjC functions for GestureRecognizer #2 ->>>
#objc func handlePanGesture2(gesture2: UIPanGestureRecognizer) {
let answerOptionView2 = gesture2.view!
switch gesture2.state {
case .began, .changed:
let translation2 = gesture2.translation(in: self.view)
buttonLabel2.transform = CGAffineTransform(translationX: translation2.x, y: translation2.y)
case .ended:
if answerOptionView2.frame.intersects(questionMarkView.frame) {
answerOptionView2.frame = questionMarkView.frame
answerOptionView2.isUserInteractionEnabled = false
} else {
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1, options: .curveEaseIn, animations: {
self.buttonLabel2.transform = .identity
})
}
default: break
}
}
#objc func handlePanGesture3(gesture3: UIPanGestureRecognizer) {
let answerOptionView3 = gesture3.view!
switch gesture3.state {
case .began, .changed:
let translation3 = gesture3.translation(in: self.view)
buttonLabel3.transform = CGAffineTransform(translationX: translation3.x, y: translation3.y)
case .ended:
if answerOptionView3.frame.intersects(questionMarkView.frame) {
answerOptionView3.frame = questionMarkView.frame
answerOptionView3.isUserInteractionEnabled = false
} else {
UIView.animate(withDuration: 0.4, delay: 0, usingSpringWithDamping: 0.3, initialSpringVelocity: 1, options: .curveEaseIn, animations: {
self.buttonLabel3.transform = .identity
})
}
default: break
}
}
}`
[1]: https://i.stack.imgur.com/UAFpS.jpg - this is screen of my project, if necessary I can provide you with the whole project itself.

Related

(UILabel) nil, Cannot insert text in the label

Good day,
I seem to have such a simple problem but I just can not wrap my head around it.
I have a container view inside a view controller. In that container I have few labels. The container has its own view controller. In the view controller for the container I have a timer running and I want that label to show the timer. But every time I use that label the app crashes with
"Thread 1: Fatal error: Unexpectedly found nil while unwrapping an Optional value"
If I comment the line out that has this label then everything runs fine.
#IBOutlet weak var timeLabel: UILabel!
var counter = 0.0
var timer = Timer()
var isRunning = false
func startStopTimer () {
if isRunning {
timer.invalidate()
isRunning = false
}else {
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
isRunning = true
}
}
#objc func updateTimer() {
counter = counter + 0.1
timeLabel.text = String(counter)
}
This is the first time I play around with container view in the Main storyboard.
Anyone that knows what I am doing wrong or has suggestion what I can try to change?
Thanks
Jonas
Full Code
class MainViewController: UIViewController {
#IBOutlet weak var topContainer: UIView!
#IBOutlet weak var informationContainer: UIView!
#IBOutlet weak var startStopButtonOutlet: UIButton!
let informationContainerVC = InformationContainerViewController()
override func viewDidLoad() {
super.viewDidLoad()
setupView()
}
func setupView() {
topContainer.layer.cornerRadius = 5
topContainer.layer.masksToBounds = true
informationContainer.layer.cornerRadius = 5
informationContainer.layer.masksToBounds = true
startStopButtonOutlet.layer.cornerRadius = 5
startStopButtonOutlet.layer.masksToBounds = true
}
#IBAction func startStopButton_TouchUpInside(_ sender: UIButton) {
informationContainerVC.startStopTimer()
UIButton.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseOut], animations: {
sender.transform = CGAffineTransform.identity
}, completion: nil)
if informationContainerVC.isRunning {
startStopButtonOutlet.setTitle("Push to Pause", for: .normal)
}else {
startStopButtonOutlet.setTitle("Push to Start", for: .normal)
}
}
#IBAction func startStopButton_TouchDown(_ sender: UIButton) {
UIButton.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseIn], animations: {
sender.transform = CGAffineTransform(scaleX: 0.9, y: 0.9)
if self.informationContainerVC.isRunning {
sender.backgroundColor = UIColor.white.withAlphaComponent(0.5)
}else {
sender.backgroundColor = UIColor.green.withAlphaComponent(0.8)
}
}, completion: nil)
}
#IBAction func startStopButton_TouchUpOutside(_ sender: UIButton) {
UIButton.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseOut], animations: {
sender.transform = CGAffineTransform.identity
}, completion: nil)
}
}
Here is the code for the container view controller
class InformationContainerViewController: UIViewController {
#IBOutlet weak var timeLabel: UILabel!
var counter = 0.0
var timer = Timer()
var isRunning = false
func startStopTimer () {
if isRunning {
timer.invalidate()
isRunning = false
}else {
timer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(updateTimer), userInfo: nil, repeats: true)
isRunning = true
}
}
#objc func updateTimer() {
counter = counter + 0.1
timeLabel.text = String(counter)
}
}
When you say let informationContainerVC = InformationContainerViewController() you are creating a new instance of InformationContainerViewController that is not linked to the storyboard, so none of the outlets are set.
You need to get a reference to the view controller instance that is actually in your container view. You can do this in prepare(for segue:); If you look at your storyboard you will see that there is an embed segue that links your containing view controller to the contained view controller.
In your MainViewController:
var informationContainerVC: InformationContainerViewController?
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let destVC = segue.destination as? InformationContainerViewController {
self.informationContainerVC = destVC
}
}
#IBAction func startStopButton_TouchUpInside(_ sender: UIButton) {
informationContainerVC?.startStopTimer()
UIButton.animate(withDuration: 0.1, delay: 0.0, options: [.allowUserInteraction, .curveEaseOut], animations: {
sender.transform = CGAffineTransform.identity
}, completion: nil)
if informationContainerVC?.isRunning {
startStopButtonOutlet.setTitle("Push to Pause", for: .normal)
} else {
startStopButtonOutlet.setTitle("Push to Start", for: .normal)
}
}
Now you will have a reference to the correct view controller instance

how to make a sliding door animation in Xcode

I am very new to Xcode, so this could be something very basic that i'm just not finding, but i'm trying to create an app that moves 2 images away from each other (at least one width away) when the user taps the screen, and then have the images return to their original positions when the user taps the screen again, however, i have been unable to find out how to move an image in a specific direction, a specific distance.
i'm also new to Stack Overflow, so i'm sorry if i'm doing something wrong
I am breaking the rules by giving up the answer when it appears you have not googled enough. The general rule is to show the code you have attempted and indicate what part you are having issues with.
The code below will achieve what you are attempting to do.
import UIKit
class slidingIamgesViewController: UIViewController {
#IBOutlet weak var topImage: UIImageView!
#IBOutlet weak var bottomImage: UIImageView!
var doubleTap: Bool! = false
//MARK: View LifeCycle
override func viewDidLoad() {
super.viewDidLoad()
let singleFingerTap = UITapGestureRecognizer(target: self, action: #selector(slidingIamgesViewController.handleSingleTap(_:)))
self.view.addGestureRecognizer(singleFingerTap)
}
// MARK: gestutre recognizer
func handleSingleTap(_ recognizer: UITapGestureRecognizer) {
if (doubleTap) {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y += basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y -= basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images Moved back!")
})
doubleTap = false
} else {
UIView.animate(withDuration: 0.7, delay: 1.0, options: .curveEaseOut, animations: {
var basketTopFrame = self.topImage.frame
basketTopFrame.origin.y -= basketTopFrame.size.height
var basketBottomFrame = self.bottomImage.frame
basketBottomFrame.origin.y += basketBottomFrame.size.height
self.topImage.frame = basketTopFrame
self.bottomImage.frame = basketBottomFrame
}, completion: { finished in
print("Images sperated!")
})
doubleTap = true
}
}
}
Make sure in your storyboard to added a Tap Gesture Recognizer.

Unable to get UIImageView from UITabBar swift

I am following tutorial to animate tab bar images. The given line of code doesn't work for me
secondItemView.subviews.first as! UIImageView
as it gets data of type UITabBarButtonLabel and not UIImageView.
I added tag value 1 so that I can get UIImageView using tag value
secondItemView.viewWithTag(1)
returns nil. What is the other way to get UIImageView's reference?
Code
#objc public class MTMainScreenTabsController : UITabBarController {
var secondItemImageView: UIImageView!
public override func viewDidLoad() {
super.viewDidLoad()
let secondItemView = self.tabBar.subviews[0]
print(secondItemView.subviews)
print(secondItemView.viewWithTag(1))
//self.secondItemImageView = secondItemView.subviews.secon
var item: UITabBarItem! = self.tabBar.items![0]
//self.secondItemImageView = secondItemView.viewWithTag(1) as! UIImageView
}
public override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
}
public override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 1 {
self.secondItemImageView.transform = .identity
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: .curveEaseInOut, animations: {
() -> Void in
//let rotation = CGAffineTransformMakeRotation(CGFloat(M_PI_2))
let rotation = CGAffineTransform.init(rotationAngle: CGFloat(Double.pi/2))
self.secondItemImageView.transform = rotation
}, completion: nil)
}
}
}
It's not a good practice to strictly depend on the order of subviews as it may change in the future. If you are sure that secondItemView.subviews contain an instance of UIImageView you can find it like this:
let imageView = secondItemView.subviews.flatMap { $0 as? UIImageView }.first
An imageView variable would be an optional containing either nil or an actual UIImageView if there was any subview of this type. Flat map would iterate through subviews and try to cast each to UIImageView - if it fails the result will be filtered out, if not it will then be put in the result array from which you're taking first element.
Use My code this one is help you
class AnimatedTabBarController: UITabBarController {
var secondItemImageView: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
let secondItemView = self.tabBar.subviews[1]
self.secondItemImageView = secondItemView.subviews.first as! UIImageView
self.secondItemImageView.contentMode = .center
}
override func tabBar(_ tabBar: UITabBar, didSelect item: UITabBarItem) {
if item.tag == 1{
//do our animations
self.secondItemImageView.transform = CGAffineTransform.identity
UIView.animate(withDuration: 0.7, delay: 0, usingSpringWithDamping: 0.5, initialSpringVelocity: 1, options: UIViewAnimationOptions(), animations: { () -> Void in
let rotation = CGAffineTransform(rotationAngle: CGFloat(M_PI_2))
self.secondItemImageView.transform = rotation
}, completion: nil)
}
}
}

Why are the animated UI Labels in my code starting from within the view?

I'm a very new-to-code learner. I've been taking some online courses, and came across this project recently.
I basically copied the code as I saw it on the screen, as the project files weren't available to download.
The animation is supposed to bring the UILabels from outside the view and position them within the view.
What seems to happen however, is the labels are starting within the view screen and animate outward and beyond.
I've copied the project below. Any help is so very appreciated.
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var helloWorld: UILabel!
#IBOutlet weak var secondLabel: UILabel!
#IBOutlet weak var hiddenLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
helloWorld.center.y -= view.bounds.height
secondLabel.center.y += view.bounds.height
hiddenLabel.alpha = 0.0
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
// Animate Hello World label
UIView.animateWithDuration(1.5, animations: {
self.helloWorld.center.y += self.view.bounds.height
}, completion: { finished in
self.secondAnimation()
})
// Animate background color change
UIView.animateWithDuration(2.0, delay: 1.5, options: [], animations: {
self.view.backgroundColor = UIColor.yellowColor()
}, completion:nil)
}
// Animate second Label
func secondAnimation() {
UIView.animateWithDuration(1.5, delay: 0.0, options: [], animations: { () -> Void in
self.secondLabel.center.y -= self.view.bounds.height
}, completion:nil)
}
func backgroundColor() {
UIView.animateWithDuration(2.5, animations: {
self.view.backgroundColor = UIColor.blackColor()
}, completion: nil)
UIView.animateWithDuration(1.0, delay: 1.5, options: [], animations: {
self.hiddenLabel.alpha = 1.0
}, completion:nil)
}
}
I would check hello world.center.y value in viewWillAppear, before you subtract the view.bounds.height. If center.y value is large and positive, then the subtraction might be setting the center.y value to be inside of the view. If this is the case, then you would want to change the subtraction in viewWillAppear to addition and then the addition in the viewDidAppear animation to subtraction.

how to animate an existing button in swift

I'd like to animate an existing UIButton in swift, the goal is to let get the attention of the user to this button when he taps the view controller. The animation is supposed to start from the top in the view controller and go to the initial position of the button.
I did the following scenario but it's not working:
import UIKit
class ViewController: UIViewController {
#IBOutlet var dismissButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
dismissButton = UIButton()
var tapGesture = UITapGestureRecognizer(target: self, action: Selector("handleTapGesture:"))
view.addGestureRecognizer(tapGesture)
}
func handleTapGesture(tapGesture: UITapGestureRecognizer) {
println("tap")
UIView.animateWithDuration(2.0, delay: 0.5, usingSpringWithDamping: 0.2, initialSpringVelocity: 0.0, options: nil, animations: {
self.dismissButton.center = CGPoint(x: 200, y: 90 + 200)
}, completion: nil)
}
}
Any suggestion?
Thank you!
Step 1: Take IBOutlet of your Top Constraint of button
Step 2: Update your Constraint's constant by using constraintName.constant property
Step 3: In animation block type self.view.layoutIfNeeded()
Here is an simple example: https://stackoverflow.com/a/26040569/3411787

Resources