UIScrollView setContentOffset not right - swift

I have an UIScrollView with some labels inside. I can move the scroll view to the other 'page' with an button. But the offset isn't right when I push it too fast.
My code to move the scrollview to the next page:
#IBAction func moveToRight(_ sender: Any) {
let size = Int(scrView.contentOffset.x) + Int(scrView.frame.size.width);
scrView.setContentOffset(CGPoint(x: size, y: 0), animated: true)
}
The offset isn't right when I push it too fast. It looks like that the current animation stops and is going to perform the next one from it's current (unfinished) position.
Does anybody has an solution for this?

I don't have time to test but I think you are on the right track about the main reason of the issue. Since animated is set to true, my guess is contentOffset.x has not yet been set to its ultimate value until animation is finished.
Why don't you change your logic a bit and create a property which remembers the current page at where you last scrolled:
var currentPage: Int = 0
Then every time you moved right, increment the current page number if possible:
#IBAction func moveToRight(_ sender: Any) {
let maxX = scrollView.contentSize.x - scrollView.frame.width
let newX = CGFloat(currentPage + 1) * scrollView.frame.width
if newX <= maxX {
currentPage = currentPage + 1
scrollView.setContentOffset(CGPoint(x: newX, y: 0), animated: true)
}
}

Related

smooth animate between 3 locations swift 3

still i'm bignner in swift and ios
i'm building my unversity project, but i faced some issues with animating, firstly here is my code:
#IBAction func tab1(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations:{
self.slider.center.x -= self.tab1.bounds.width
})
}
#IBAction func tab2(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations:{
self.slider.center.x += self.tab2.bounds.width
})
}
#IBAction func tab3(_ sender: Any) {
UIView.animate(withDuration: 0.5, animations:{
self.slider.center.x += self.tab3.bounds.width
})
}
you can see how much it's basic , and there is many problems like if slider in tab1 and click on tab 3 slider will move one step (to tab2) than user needs press one more time, + if you are in tab2 and press on tab2 again slider move again , many bugs in my code, i search for solutions but no luck .
i want adjust it to be animate to correct place and validate it, that means if slider in correct place the slider will not move.
i hope find some help from swift developers
You can just use the button frame to calculate x:
let tab = sender as! UIView
UIView.animate(withDuration: 0.5, animations:{
self.slider.center.x = tab.frame.midX
})

How to rotate an image cumulatively in Swift ? (Xcode 8)

I am trying to make a button in my view that rotates my image of a gimmicky clock by 45 degrees on each tap of the "rotate" button. All tutorials & answers that I researched online only go to the point where you hit the button once, then it resets (in my case, app only does the action once, then does nothing). So I am trying to make it go from 0 to 45 degrees on the 1st tap, 45 to 90 degrees on 2nd tap, and so on, infinitely, ie store the result every time the button is tapped, then continue from there. Any thoughts? Here is my simple code. Any help would be appreciated as am a newbie in coding - many thanks
#IBAction func Rotate(_ sender: AnyObject) {
let rotation: CGFloat = 0.785398
UIView.animate(withDuration: 0.5) {
self.myWatchImage.transform = CGAffineTransform(rotationAngle: rotation)
}
}
Apply rotated:by: to the current transform to get the next one:
#IBAction func doRotate(_ sender: UIButton) {
let rotation = CGFloat.pi / 4.0
let transform = myWatchImage.transform
let rotated = transform.rotated(by: rotation)
UIView.animate(withDuration: 0.5) {
self.myWatchImage.transform = rotated
}
}

Only allowing scrolling up in a Table View?

Is there a way to make it so the user can only scroll up in a table view? Right now I only see an option to scroll both up and down.
What does you mean under "scroll only up"?
If it's mean that content start from bottom and user can only scroll up, you can try this:
1) reverse array of objects that you use for feeding tableView
if shouldReverse {
var reversed = myObjects.reverse()
myObjects.removeAll(keepCapacity: false)
myObjects.splice(reversed, atIndex: 0)
}
2) implement scrollViewDidScroll: method from UIScrollViewDelegate protocol, and restrict content offset by it's y position
func scrollViewDidScroll(scrollView: UIScrollView) {
if shouldReverse && scrollView.contentOffset.y < 0 {
scrollView.contentOffset = CGPoint(x: scrollView.contentOffset.x, y: 0)
}
}

Keeping an image on screen in Swift

I have a game where I use a UISwipeGestureRecognizer to move the screen. I am trying to figure out how to make it so that the image that I am swiping cannot move off the screen. I have looked around the internet and have not been able to find anything.
Here is my swipe method:
func swipedRight(sender: UISwipeGestureRecognizer) {
timer = NSTimer.scheduledTimerWithTimeInterval(1.0, target: self, selector: Selector("moveBackground"), userInfo: nil, repeats: true)
if imageView.frame.origin.x > 5000 {
imageView.removeFromSuperview()
}
}
Here is my moveBackground method:
func moveBackground() {
self.imageView.frame = CGRect(x: imageView.frame.origin.x - 100, y: imageView.frame.origin.y, width: 5500, height: UIScreen.mainScreen().bounds.height)
}
The only things I have tried so far is to check if the view is in a certain position and remove it if it is but that hasn't worked. I added that code to the swipe method.
It will be very helpful if you post your class code here, so we can be more specific helping you.
By the way, you should check the object position each time the function is called, before move it, taking in mind its size.
Lets say you have a playing cards game and you are swiping a card along the board.
To avoid swiping the card off the board (off the screen) you will have to do something like this:
// I declare the node for the card with an image
var card = SKSpriteNode(imageNamed: "card")
// I take its size from the image size itself
card.physicsBody = SKPhysicsBody(rectangleOfSize: card.size)
// I set its initial position to the middle of the screen and add it to the scene
card.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2)
self.addChild(card)
// I set the min and max positions for the card in both X and Y axis regarding the whole scene size.
// Fit it to your own bounds if different
let minX = card.size.width/2
let minY = card.size.height/2
let maxX = self.frame.size.width - card.size.height/2
let maxY = self.frame.size.width - card.size.height/2
// HERE GOES YOUR SWIPE FUNCTION
func swipedRight(sender: UISwipeGestureRecognizer) {
// First we check the current position for the card
if (card.position.x <= minX || card.position.x >= maxX || card.position.y <= minY || card.position.y >= maxY) {
return
}
else {
// ...
// YOUR CODE TO MOVE THE ELEMENT
// ...
}
}
I hope this helps!
Regards.

UIScrollView with "Circular" scrolling

I am trying to make "Circular" scrolling in my UIScrollView, but unsuccessful.
What I want to do:
if uiscrollview reaches end, it should move to start
if uiscrollview at start and moving back, it should move to end
Appending scrollview isn't good way in my situation (other methods should get "page id")
Have you any ideas?
I've implemented this method, but it requires paging enabled. Lets assume you have five elements A,B,C,D and E. When you set up your view, you add the last element to the beginning and the first element to the end, and adjust the content offset to view the first element, like this E,[A],B,C,D,E,A. In the UIScrollViewDelegate, check if the user reach any of the ends, and move the offset without animation to the other end.
Imagine the [ ] indicates the view being shown:
E,A,B,C,[D],E,A
User swipes right
E,A,B,C,D,[E],A
User swipes right
E,A,B,C,D,E,[A]
Then, automatically set the content offset to the second element
E,[A],B,C,D,E,A
This way the user can swipe both ways creating the illusion of an infinite scroll.
E,A,[B],C,D,E,A
Update
I've uploaded a complete implementation of this algorithm. It's a very complicated class, because it also has on-click selection, infinite circular scroll and cell reuse. You can use the code as is, modify it or extract the code that you need. The most interesting code is in the class TCHorizontalSelectorView.
Link to the file
Enjoy it!
Update 2
UICollectionView is now the recommended way to achieve this and it can be used to obtain the very same behavior. This tutorial describes in details how to achieve it.
Apple has a Street Scroller demo that appears to have exactly what you want.
There's also a video from WWDC 2011 that demos their demo. ;) They cover infinite scrolling first in the video.
Try to use following code..
Sample code..
- (void)scrollViewDidEndDecelerating:(UIScrollView *)sender
{
if (scrollView.contentOffset.x == 0) {
// user is scrolling to the left from image 1 to image n(last image).
// reposition offset to show image 10 that is on the right in the scroll view
[scrollView scrollRectToVisible:CGRectMake(4000,0,320,480) animated:NO];// you can define your `contensize` for scrollview
}
else if (scrollView.contentOffset.x == 4320) {
// user is scrolling to the right from image n(last image) to image 1.
// reposition offset to show image 1 that is on the left in the scroll view
[scrollView scrollRectToVisible:CGRectMake(320,0,320,480) animated:NO];
}
}
Hope, this will help you...
With reference from #redent84, find the code logic.
override func viewWillAppear(_ animated: Bool)
{
super.viewWillAppear(animated)
scrollView.contentSize = CGSize(width: scrollView.frame.width * CGFloat(imagesArray.count), height: scrollView.frame.height)
scrollView.isPagingEnabled = true
// imagesArray.insert(imagesArray.last as? UIImage, at: 0)
// imagesArray.insert(imagesArray.first as? UIImage, at: imagesArray.count - 1)
var testArr = ["A", "B", "C", "D"]
let firstItem = testArr.first
let lastItem = testArr.last
testArr.insert(lastItem as! String, at: 0)
testArr.append(firstItem as! String)
print(testArr)
for i in 0..<imagesArray.count{
let imageview = UIImageView()
imageview.image = imagesArray[i]
imageview.contentMode = .scaleAspectFit
imageview.clipsToBounds = true
let xPosition = self.scrollView.frame.width * CGFloat(i)
imageview.frame = CGRect(x: xPosition, y: 0, width: self.scrollView.frame.width, height: self.scrollView.frame.height)
print(imageview)
scrollView.addSubview(imageview)
print("Imageview frames of i \(i) is \(imageview.frame)")
}
newStartScrolling()
}
func newStartScrolling()
{
ViewController.i += 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
print("X is \(x) and i is \(ViewController.i)")
if ViewController.i == imagesArray.count - 1 {
ViewController.i = 0
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
//scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: false)
print("X (rotate) is \(x) and i is \(ViewController.i)")
scrollView.contentOffset.x = x
self.newStartScrolling()
}
}
func backScrolling() {
ViewController.i -= 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: true)
print("X is \(x) and i is \(ViewController.i)")
if ViewController.i <= 0 {
ViewController.i = imagesArray.count - 1
let x = CGFloat(ViewController.i) * scrollView.frame.size.width
scrollView.setContentOffset(CGPoint(x: x, y: 0), animated: false)
print("X (rotate) is \(x) and i is \(ViewController.i)")
self.backScrolling()
//scrollView.contentOffset.x = x
return
}
}