I have loaded 31 images into a UIImageView and would like to use the UIPanGestureRecognizer to have the user simply pan up and down to change the images. The only example I could find is in Obj-C and can seem to figure out how to make it work. So far I have try to used the following code:
#IBAction func panPerformed(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
let translation = sender.translation(in: self.view).y
//print (translation)
if translation > 0 { //swipe up
} else { //swipe down
var pos = Int(abs(translation / panThreshold))
if pos < chartImageArray.count {
print (pos)
chartView.image = chartImageArray[pos]
}
}
This does scroll through, but when you release it obviously go back to 0. Is there some better way to achieve this functionality?
I was try to figure out a way to track the transition number and somehow increase or decrease an index number that would correlate to the image array.
Thanks
Craig
EDIT: Here is the code I used to get the functionally
#objc func panPerformed(_ sender: UIPanGestureRecognizer) {
if sender.state == .began || sender.state == .changed {
let velocity = sender.velocity(in: self.view).y
var count = 0
if velocity > 0 {
count -= 1
} else {
count += 1
}
movePictures(count)
} else if sender.state == .ended { //release swipe
}
}
func movePictures(_ ind:Int) {
// print (index)
if index >= 0 {
index = index + ind
} else {
index = arrayCount * panThreshold
}
if index <= arrayCount * panThreshold {
index = index + ind
} else {
index = 0
}
}
Related
Tap gesture get triggered by the below function;
#objc func handleTapGR(tapGR: UITapGestureRecognizer) {
let tapLocation = tapGR.location(in: nil)
let moveNext = tapLocation.x > frame.width / 2 ? true : false
moveNext ? userCards.nextImage() : userCards.previousImage()
}
UserCardViewModel:
...
var imageIndex: Int = 0 { didSet{ imageIndexObserver?(imageIndex, userImages[imageIndex]) } }
var imageIndexObserver: ((Int, UIImage) -> () )?
mutating func nextImage() {
imageIndex = imageIndex + 1 >= userImages.count ? 0 : imageIndex + 1
}
mutating func previousImage() {
imageIndex = imageIndex - 1 < 0 ? userImages.count - 1 : imageIndex - 1
}
Related closure needs to be triggered when the index is changed.
Below object is in related view.
var userCards: UserCardViewModel! {
didSet{
userCards.imageIndexObserver = { index, image in
self.showingImage.image = self.userCards.userImages[index]
print(index)
}
}
}
I am not able to get printed above the index statement at the first two taps.When I press right side on image (related array count is 2)
At first tap; nothing happens.
At second tap; prints 0.
At third, it prints index = 1.
I am initializing like that (from related viewcontroller)
convenience init(userCards: UserCardViewModel) {
self.init(frame: .zero)
self.userCards = userCards
configure()
}
I have added gesture on view. When you tap right side index count will increase (left side will decrease), once it reached on array count then it will starts from 0 again.
class ViewController: UIViewController {
var userCards = UserCardViewModel() {
didSet{
userCards.imageIndexObserver = { index, image in
//self.showingImage.image = self.userCards.userImages[index]
print(index) //<------- Here you will get index
}
}
}
override func viewDidLoad() {
super.viewDidLoad()
let img1 = UIImage(systemName: "arrow.up.right.video")
let img2 = UIImage(systemName: "arrow.up.right.video")
let img3 = UIImage(systemName: "arrow.up.right.video")
userCards.userImages = [img1!,img2!,img3!]
let tap = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
self.view.addGestureRecognizer(tap)
}
#objc func handleTap(_ tapGR: UITapGestureRecognizer) {
let tapLocation = tapGR.location(in: nil)
let moveNext = tapLocation.x > self.view.frame.width / 2 ? true : false
moveNext ? userCards.nextImage() : userCards.previousImage()
}
}
This is Model class
struct UserCardViewModel {
var userImages : [UIImage] = []
var imageIndex: Int = 0 {
didSet {
imageIndexObserver?(imageIndex, userImages[imageIndex])
} }
var imageIndexObserver: ((Int, UIImage) -> () )?
mutating func nextImage() {
imageIndex = imageIndex + 1 >= userImages.count ? 0 : imageIndex + 1
}
mutating func previousImage() {
imageIndex = imageIndex - 1 < 0 ? userImages.count - 1 : imageIndex - 1
}
}
Initially imageIndex is 0
1) At start when you tap on right side imageIndex will become 1
2) At start when you tap on left side imageIndex will become (yourArray.count - 1)
Download demo from here
var players : [Player]?
var currentplayerIndex = 0
var currentQuestion : QuestionObject!
var questions = Questions()
var score = 0
var currentQuestionPos = 0
func updateUi() {
if let score = players?[currentplayerIndex].score {
playerPoints.text = "Points: \(score)"
}
}
func loadnextQuestion () {
if(currentQuestionPos < questions.questions.count) {
currentQuestionPos += 1
if currentplayerIndex < players!.count - 1 {
currentplayerIndex += 1
} else {
currentplayerIndex = 0
}
playerTurn.text = (players?[currentplayerIndex].name)
currentQuestion = questions.questions[currentQuestionPos]
questionText.text = currentQuestion.question
}
}
#IBAction func submitButtonPressed(_ sender: UIButton) {
let i = pickerView.selectedRow(inComponent: 0)
if(currentQuestion.answer == i) {
players?[currentplayerIndex].score += 1
loadnextQuestion()
updateUi()
} else {
updateUi()
loadnextQuestion()
}
}
}
My score displays only 0 all the time.
Does not increase when the answer is right.
All the added players get 1 question each but the sore is still 0 for all the players.
Your code doesn't work because you are doing this series of things when the answer is correct:
players?[currentplayerIndex].score += 1
loadnextQuestion()
updateUi()
The player's score is indeed incremented. But notice that the next thing you do is loadnextQuestion(), which increments currentplayerIndex. This causes updateUi to update the label's text to the score of the next player, not of the player that just answered the question correctly.
One way to fix this is to swap the last two lines:
players?[currentplayerIndex].score += 1
updateUi()
loadnextQuestion()
Notice that this is the same order as in the else branch, which you wrote correctly.
This causes the label to always display the score of the last player, which might not be desirable. Assuming there's not like 100 players or something crazy like that, I think it would be a much better UX if you display all the player's scores:
func updateUi() {
if let scores = players?.map({ "\($0.score)" }) {
playerPoints.text = "Points: \(scores.joined(separator: ", "))"
}
}
You can replace the button action work
#IBAction func submitButtonPressed(_ sender: UIButton) {
let i = pickerView.selectedRow(inComponent: 0)
if(currentQuestion.answer == i) {
players?[currentplayerIndex].score += 1
updateUi()
loadnextQuestion()
} else {
updateUi()
loadnextQuestion()
}
}
Here is the example picture:
Now I want to remove the overlay between 5 & 6 annotations. Here is the code snippet what I tried so far:
Function to create polyline
func createPolyline() {
if (arrTempFootStepsLocation.count>0){ //This array contains all the coordinates from starting annonation 1 to 6
let points:[CLLocationCoordinate2D] = arrTempFootStepsLocation
let geodesic = MKGeodesicPolyline(coordinates: points, count: points.count)
mapService.addOverlay(geodesic)
}
}
Function to create annotations
func addAnnotations() {
if let arrProof = arrTempProof{ //This array contains 6 coordinates which are taken from arrTempFootStepsLocation only to create annotation
for (index , modelPhotoProof) in arrProof.enumerated() {
if (modelPhotoProof.locationPoint?.latitude != 0.0 && modelPhotoProof.locationPoint?.longitude != 0.0){
let CLLCoordType = CLLocationCoordinate2D(latitude: modelPhotoProof.locationPoint!.latitude, longitude: modelPhotoProof.locationPoint!.longitude)
let anno = MyMKPointAnnotation()
anno.tag = index
anno.coordinate = CLLCoordType
mapService.addAnnotation(anno)
}
}
}
}
func mapView(_ mapView: MKMapView, didSelect view: MKAnnotationView) {
if view.annotation is MyMKPointAnnotation {
if arrRemovalAnnotationPoint.count<2{ //This array contains selected annotation coordinates
arrRemovalAnnotationPoint.append((view.annotation?.coordinate)!)
print("Annotation point inserted.")
}
}
}
Delete overlay between two annotations
#IBAction func btnDeletePolylinesPressed(_ sender:UIButton){
if arrRemovalAnnotationPoint.count == 2 {
//Find the index of those two annotations from arrTempFootStepsLocation
let firstAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.first?.latitude && $0.longitude == arrRemovalAnnotationPoint.first?.longitude})
let lastAnnoIndex = arrTempFootStepsLocation.index(where: {$0.latitude == arrRemovalAnnotationPoint.last?.latitude && $0.longitude == arrRemovalAnnotationPoint.last?.longitude})
//prepare array to remove coordinates between those two annotations
if let first = firstAnnoIndex, let last = lastAnnoIndex{
var arrToRemoved = [CLLocationCoordinate2D]()
if first > last {
arrToRemoved = Array(arrTempFootStepsLocation[last...first])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
else {
arrToRemoved = Array(arrTempFootStepsLocation[first...last])
for i in 0..<arrToRemoved.count{
let itemToBeRemoved = arrToRemoved[i]
for j in 0..<arrTempFootStepsLocation.count - 1 {
let itemOriginal = arrTempFootStepsLocation[j]
if (itemToBeRemoved.latitude == itemOriginal.latitude && itemToBeRemoved.longitude == itemOriginal.longitude){
arrTempFootStepsLocation.remove(at: j)
}
}
}
mapService.removeOverlays(mapService.overlays)
self.perform(#selector(self.createPolyline()), with: nil, afterDelay: 0.8)
}
}
arrRemovalAnnotationPoint.removeAll()
}
}
Now the problem is when I press delete button its doesn't clear all coordinates between these annotations. Now when I redraw the polyline it still connects between these annotations.
Whats wrong in my code? Is there any other way to do this? Any help will be appreciated.
remove the particular index in (arrRemovalAnnotationPoint) array
I am trying to change the length of a game by making an object move faster or slower through a variable on a segmented control but i get the error Expression type '#lvalue CGPoint' is ambiguous without more context.
#IBAction func control(sender: UISegmentedControl) {
if sender.selectedSegmentIndex == 0{
//variable changes in order to change the distance "purple" moves
var length: Int = 10;
} else if sender.selectedSegmentIndex == 1{
var length: Int = 20;
} else{
var length: Int = 30;
}
}
#IBAction func Red(sender: AnyObject) {
var l: Int;
if self.Purple.center.y > self.TopFinish.center.y && self.Purple.center.y <
self.Finish.center.y && y==0{
UIImageView.animate(withDuration:0.75, delay: 0, options: .curveLinear,
animations: {
//i got the error below:
self.Purple.center.y += length;
}, completion: nil)
}
if self.Purple.center.y >= self.Finish.center.y{
Label.text="Red Wins!"
Restart.isHidden=false;
self.Purple.center.y = self.Finish.center.y
y=1
}
}
You never defined lenght in your func Red
You need to make a global variable or define it inside the function.
I'm new and trying to learn and code my first app. A simple app to get started...So I'm trying to make a loop that will perform a series of functions completely for a certain number of times (rounds). I've figured out the body of code and I'm trying to implement DispatchGroup() to wait on the functions to perform:
#IBAction func buttonPush(_ sender: Any) {
let group = DispatchGroup()
while rounds >= 0 {
group.enter()
DispatchQueue.main.async {
if self.isGameStarted == 0 {
self.setTimer()
self.resetVariables()
self.runClock()
self.roundLabel.text = String(self.self.rounds)
group.leave()
} else if self.isGameStarted >= 1{
self.self.isGameStarted = 0
self.timer.invalidate()
group.leave()
}
}
group.notify(queue: .main) {
self.rounds -= 1
}
}
}
Here was the original body pre DispatchGroup(). The issue was that the while loop was completing before the functions could run, I think (best guess). I'm using the latest version of swift as of 11/19/17.
#IBAction func buttonPush(_ sender: Any) {
while rounds >= 0 {
if isGameStarted == 0 {
setTimer()
resetVariables()
runClock()
roundLabel.text = String(rounds)
} else if isGameStarted >= 1{
isGameStarted = 0
timer.invalidate()
rounds -= 1
}
}
}
12/13/17 edit. This works for each button press and while loop commented out. However, with while loop uncommented it goes into infinite loop...
#IBAction func buttonPush(_ sender: Any) {
//while rounds >= 0 {
if isGameStarted == 0 {
setTimer()
resetVariables()
runClock()
roundLabel.text = String(rounds)
DispatchQueue.main.asyncAfter(deadline: .now() + Double(timeRoundTot){
self.rounds -= 1
} else if isGameStarted >= 1{
isGameStarted = 0
timer.invalidate()
}
}
//}
}