I want to create a view in which I like to have animation such as the one present in iOS 8 Weather app.
I try to explain more what I have done. If anything is incorrect please guide me.
In the top I put a label for the temperature. (The big one)
Below that label, I put another label to show some text. In the Weather app, there is the horizontal scrollview showing the hourly forecast.
Next is the Table view.
What I want to achieve is that when I start scrolling, the first label disappear smoothly and the second one go to top of the screen and the TableView stretches to show more content.
When I scroll back to the top, I want the whole process to revert.
What is the best way to do this?
I've recently re-created the iOS8 Weather app's scrolling effect for an app I'm creating.
The complete code is too long to post here but for anyone who's interested, I've put it on GitHub. You can download and run the project to see how it looks. Improvements to the code are welcome:
UIScrollView with masked content.
It works like this:
You have one scrollview (the size of the screen), which contains a subview (the mask) which in turn has a subview (the content). We also add a large label to the top of the screen (in the Weather app, this displays the temperature).
Then you use the scrollViewDidScroll delegate method to keep the mask fixed to the screen as the user scrolls. You also use this method to stretch the mask upwards at first.
Fixing the mask to the screen means that the mask's subviews (our content) also becomes fixed. But we want it to scroll, so we do the opposite to the content of what we did to the mask (again, using scrollViewDidScroll).
We need the mask to actually behave as a mask, so we set clipsToBounds = YES on the mask.
Finally, we use the scrollview's offset during scroll to move and fade the big label at the top of the screen.
To make it behave exactly like the iOS8 Weather app, we need to also do the following:
Cancel any scroll touches that happen above the mask, i.e. over the large temperature display.
Ensure that the initial scroll that moves the temperature display is completed programatically if the user doesn't complete it.
Add a sideways-scrolling subview which is anchored to the top of the mask.
I haven't done these yet, though.
Related
Sample Project
This is a sample project that showing the issue. It's storyboard based, but method of building interface doesn't matter. It's UIViewController with UIScrollView for entire screen and 128 pts height view that is on top of this UIScrollView.
Inside scroll view there is an UIView that has 2000 pts height and UIButton in the center.
Initial State
After light scroll
At the bottom of UIScrollView
Link here: https://github.com/JakubMazur/UITestsDemo
Problem
I'm trying to tap this green button with XCUITest using app.buttons["Tap Me!"].tap()
XCUITest get identifiers from elements on screen for entire scroll view that works fine.
According to this reply on a thread on Apple Developer Forum written by Apple Framework Engineer I shouldn't scroll manually to get to the button and yes, this is partially true.
What is happening when code from (1) is executed is that button is scrolled just enough to be visible on screen but it's still not hittable, because other (purple view) is on top of UIScrollView
What is working
If I run a test written like this:
func testThatDoWorkButItsSlow() {
app.scrollViews.firstMatch.swipeUp()
app.buttons[buttonLabel].tap()
}
that is scrolling up and then looks for a button this will work, but it's slow and so inaccurate that is hardly usable.
What I cannot do
Disabling userInteractions on purple view. In real example I still need touches for this (purple) view.
Questions
Is there a way to use precise scrolling in XCTest for this case?
Or is there a way to set contentOffset scrollview to other value that will make this button more centered on a screen compared to action of tap()?
Or there is a way to fast scroll to the bottom (without animations) and maybe moving only up for each element?
My recommendation here would be to use the XCUICoordinate.press(forDuration:thenDragTo:) method to scroll.
https://developer.apple.com/documentation/xctest/xcuicoordinate/1615003-press
You can create a XCUICoordinate for the yellow view, then drag it slightly upwards to expose the button and make it hittable.
In most cases, the automatic scroll should work, but it seems like in this case a manual scroll/drag is necessary.
The UI Testing should replicate human interactions. You cannot expect from a human being to scroll "153px", you can just expect to "scroll until".
You can try something like :
while (!app.buttons["Tap Me!"].isHittable) {
app.swipeUp()
}
NB: You may also want to add a condition to leave the while loop if you can't find the button after a reasonable amount of attempts
Does anyone know how to do something like that?
I would like to get a continuous scrolling of the image.
Example: https://vimeo.com/262632459
Here is a simple way of doing it.
Set it up
Create the image you want to scroll in the background. You need to make it minimum as wide as the widest screen you want to support.
Place two instances of the image you created next to each other inside a regular UIView.
Place the view on the screen with the x position at 0.
Animate
Animate the view repeatedly forever, moving the x position from 0 to -width. The width is the width of your image (just the width of one instance). Note that since you want to pull the view left, you must pull x in the negative direction.
The image will now slide left until the second instance x is at 0. At this position the animation will start over, moving the view one whole image size to the left. This movement will not be noticeable since you use two identical images.
You need to use scrollview or subclass of scrollview and in that scrollview add your image you want to scroll.
override scrollViewDidScroll delegate method of scrollview, here you can control the animation (how much user can scroll or other behaviours)
https://developer.apple.com/documentation/uikit/uiscrollviewdelegate/1619392-scrollviewdidscroll?language=objc
And if you want circular image(when image reach to last start again) add two copy of same image, One at zero position, before your acutal image and one at last position after actual image and as soon as image reaches to last remove the font image and add in the last, and if user scrolling in right direction remove the last image and add in front.
In scrollview add three imageview and keep adding and removing from the front and last based on the scroll direction. In scrollViewDidScroll you will get direction of scoll based on change in x value
I have implemented a UIScrollView for my game board, this board is a UIView consists of several images, the image of the board and some images of detail. And the zoom and scroll work well.
But what I do is that when I move the board will not let me get out of the board and see the bottom. Right now when I move the board around the edges sometimes see white, as my picture moves more than up to the stop.
What I want is to put a cap on scrollview not see what's behind only see the board.
I would also like to know if any of you know how I can do to drag images as a given within the board, as this all in a UIScrollView and I recognize there touch.
Thanks in advance
What I want is to put a cap on scrollview not see what's behind only see the board.
Yes, you can limit the visible area to just your content. Set the scroll view's bounces property to NO and make sure that the contentInset is 0. (0 is the default for contentInset, so it should be fine if you haven't changed it.)
how I can do to drag images as a given within the board
Scroll views have the option to delay touch events in the content view until the scroll view decides whether or not the user is trying to scroll. Read about the delaysContentTouches property. You'll probably want to leave that property set to YES. Beyond that, you really just need to learn to monitor touches -- you can drag a view by adjusting its position (by setting it's center, for example) as a touch moves around the screen. There's some sample code that may help you in Handling Swipe and Drag Gestures.
I've got a scrollview that allows the user to scroll between different pages and then tap on one to have it expand so that they can read the page in full, a little like how one changes tabs in Safari on the iPhone. Changing the frame size of each sub view is a bit of a pain when rotating as the scroll position is getting lost as the content size of the sub view has to change too. I was wondering if there was a more effective way of
resizing the views for entering 'viewing' mode or rotating the device.
The solution to your first problem is when you want to expand the view, pull it out of the scrollView then add it to self.view.subviews with an appropriate frame, then animate the frame to fill the screen. When done with it do the reverse, shrink it, then when its back to the appropriate size stick it back in the scrollView.
If for some reason this does not work - the scrollview is showing other stuff that gets moved when you remove the view, then instead of just removing your view from it, create a simple UIView of the same size as the view you will expand, and essentially replace the view you pull out with the "placeholder" view.
The description might be a bit confusing, I've added pictures to try to illustrate what I'm describing. Please let me know what I can clarify to help.
I have an iPad application with a main view that is a xib. The size of the xib is 1024 by 1384 and is meant to be viewed in landscape mode. There is a row of buttons that are visible at the bottom of the iPad screen. When one of the buttons is pressed I move the frame so that these buttons are now at the top of the visible portion of the screen. There are additional elements that start out offscreen but then come onscreen after the move.
The problem I'm having is that the UIButton that starts offscreen is not calling the IBAction associated with it.
I have tried to setUserEnabled to YES for it but that doesn't seem to be making any difference either. I've also tried setNeedsDisplay after the animation is complete.
Anyone have any ideas?
When you add the view to the screen, it changes the view's size to fit the visible portion on the screen. As such, your frame is smaller than the total content area of the view. Moving the frame won't do anything for you; it will move the existing visible content up, but it won't change the view to show new visible content.
Instead, you want to be changing the view's bounds.origin, which will change the visible portion of the view's content.
Edit:
Note that even though the view was shrunk, I suspect that the clipsToBounds property on your main view was set to NO. That means that it will actually continue displaying content outside of the bounds, which is why it shows up. However, hit-testing only works on the actual frames of the view. All that stuff that shows up outside the bounds is still visible, but it's not interactible.