I've got an app where I allow the user to add events to a list. The method I use to do this is via a form that I show the user in a UIPopOver.
The form has the following components (in this order, top to bottom):
Event Description Label (UILabel)
Event Description (UITextField)
Event Details Label (UILabel)
Event Details (UITextView)
Event Time Label (UILabel)
Event Time (UIDatePicker)
Save Event (Custom Button)
Cancel (Custom Button)
I'd prefer not to show a screen shot since it's a sensitive app. :-)
So........
All is well when I first show the UIPopOver. Everything appears as I wish. The overall layout is about 650 pixels tall, which is just tall enough to take up pretty much the whole screen (vertically) when in landscape mode.
When the user tries to interact with the `UIDatePicker`, everything goes swimmingly (well).
When the user taps into the Event Description (`UITextField`) field, the keyboard appears and the `UIPopOver` shrinks so that basically the `UIDatePicker` and buttons are hidden. I consider this OK, since they re-appear when the keyboard is dismissed.
The problem I am having is when the user tries to interact with the `UITextView`:
When the view of the `UIPopOver` has its `backgroundColor` property set to `[UIColor whiteColor]`, the `UITextView` initially appears with a thin black border. I like this.
When the user taps in the `UITextView` to begin editing, the `UIPopOver` resizes to accommodate the keyboard and, during the transition, the black border "shrinks up" and disappears. This is in spite of my shocks and struts being set so that all dimensions/directions are fixed and it is "glued" to the top-left.
Following this, when the user dismisses the keyboard, the border sort of restores itself to its previous position, but it now adds a thick black border on the bottom that completely obscures the label placed between the `UITextView` and the `UIDatePicker`.
When I change the background of the view of the `UIPopOver` to "`ScrollView` Textured Background Color", the border "disappearing trick" is at least less noticeable, but the black border at the bottom (following keyboard dismissal) is still present.
Questions
Has anyone encountered something like this before?
Does anyone think it's just an IB error and that I need to re-build my XIB?
Anybody got any other ideas? Googling for this issue came up fairly empty for me. If you think of better terms and find something, please let me know (gently).
Boy, I'm a stupid stupid!!! It turns out I had forgotten that I placed the UITextView inside of a UIView (inset 1 pixel on all sides) and set the background of the UIView to be black so that I could get a border effect on the UITextView. Once I remembered that, setting the shocks/struts of THAT UIView turned out to be the solution.
Sorry for wasting your time!
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
1.Show a popup when tap a button(KLCPopup)
2.Change the height of contentView(KLCpopup) when tap the 'change frame' button,and change the height successfully
3. Tap 'change frame' button again, I want change the height again, but it does not work.
the source is in https://github.com/leogeng/LabProject.git
Who can fix it?
I can fix it but it is best you do it. Let this answer be a bit more about how to debug your application.
After a very short analysis I put a breakpoint in your button action method and found out it is only called once. So it does not resize the second time because the button is not working at all.
What the candidates for that are is the button is being tempered with in ways such as interactions disabled, it is over-layed with another view which prevents the touch events, or its superviews are not correctly sized to detect touches.
After using the view debugger (there is an icon you may use in runtime which displays the view hierarchy) I can see the black button on the blue background which is on some transparent background whose size is too small and the blue view is out of bounds.
It seems you need to resize the superview of the blue view as well.
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.
I've scoured through several questions asking about how to prevent scrolling when selecting a first responder however nothing I find seems to work for this issue.
I have a UITextField above my table view at the very top of my page. When the user selects it (or when I do it programmatically) it drops down a bit, seemingly giving room for options such as "paste" or autocorrect (which I've disabled). I'd like to prevent this from happening.
I've tried setting the scroll position myself, which it initially does, but then it instantly jumps down a bit. Is there a way I can make the paste/edit bubble appear below the textfield (like it does with textfields in the header)? I'm thinking perhaps that will prevent it from jumping downwards, but I can't find information on this.
I'm completely stuck so any help is appreciated. Links, vague suggestions, whatever. Thank you.
Try embedding your text field in a scroll view the same size as your text field.
The reason why this should work is because when a text field becomes first responder, it only scrolls the scroll view that is its most recent ancestor in the view hierarchy. If it only finds the dummy scroll view, whose content size should not exceed its bounds size, then no scrolling should occur in either scroll view.
I'm using the great iOS control iCarousel's Wheel type and I want all of the buttons it contains to be usable / selectable at any time (i.e. I can tap any of them, no matter which one is currently at the top of the wheel). The user can still scroll the wheel as normal, but no matter which button they tap, it should register.
The current behavior seems inconsistent: if I tap one of the buttons directly next to the center one (to its left or right), that button moves into the center slot. Clicking one of the buttons two positions away 'sometimes' causes it to scroll to that letter, sometimes it's ignored. Any other buttons are always ignored.
Is this possible to set up, preferably without hugely modifying the class? I'm by no means an expert, but am learning every day :)
Thanks in advance.
Set carousel.centerItemWhenSelected = NO;
That will disable the behaviour where buttons other than the centre one are scrolled to the centre when you tap them.
As for the reason why some are not responding to taps at all, it is most likely because the frame of your carousel view is too small and the tap events are outside of the frame.
If you set carousel.clipsToBounds = YES; it will crop the carousel views to the frame as well so you'll be able to see exactly what size your carousel actually is.