I have a UITableViewController, one level deep inside a UINavigationController. The rootviewController is a slide out menu - library over here: https://github.com/mobivery/MVYSideMenu
The problem
When the UiTableViewController loads - its in the incorrect position and after half a second, it moves down to the correct position by it self. Looks really odd.
So, as a test, I created a new UIViewController with only boiler plate code and put it one level deep in the UiNavigationController. I then added a UIbutton to it and when the view loads, the button is always in the correct position.
So, I then added a UITableView to this UIViewController and positioned it half way down the screen. I also manually added auto layout constraints to it.
Now, re running the test - the exact same thing happens - the UITableView starts out just above its original position, and half a second later, it moves down to its correct position.
Can anyone tell me why this is happening and what could cause it?
Thanks!
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
I have an iPad app similar to the iPad Keynote with a narrow overview on the left and a paged UICollectionView of my "slides" on the right. The collection view is using the default FlowLayout. Some of these slides are standard PDFs and some are embedded UIViewControllers that have been scaled (with a CGAffineTransform) and embedded in the cell. I'd like to smoothly animate the overview sidebar offscreen and zoom the current page cell to fullscreen. The collection view should allow paged swiping at whatever size. I'm using storyboards and autolayout.
I think I need to simultaneously animate about three things:
The collection view constraints (to the sidebar) to enlarge/shrink it
The flow layout's sizeForItemAt: value
The CGAffineTransform on the embedded view controller.
I have some pieces working (a single embedded View Controller "slide" that scales correctly) but cannot get the collectionView/cell resize dance to work correctly. The cell resize animation is jerky, or ends up with the wrong offset, or works for the leftmost cell but not for other cells.
I've tried most of the suggestions in the answers to this question but with no convincing success. I can't believe it's impossible but at this point I'm considering the smoke and mirrors approach of animating a static slide and hiding it after the animation completes. The attached video - ignoring the glitches - illustrates the kind of effect I'm after:
It's worth noting, on close inspection, that Keynote cheats somewhat when it comes to swiping between slides in edit mode, and manually manages the next slide sliding onscreen, so probably doesn't use a UICollectionView.
Has anyone done anything similar, or have any suggestions for things to try?
I managed to solve this. There's a proof-of-concept GitHub repo here.
There are a few moving parts:
There are two pieces of UIView scaling code. I suspect these could be combined with suitable delegate references as the nested view controllers are embedded. The first piece scales (statically) correctly when the Collection View cell is created. The second is an animated scale/translate when the sidebar size is changed.
I added a FlowLayout subclass to remove flicker as the collection's layout is invalidated during scaling.
The sidebar-related transform in the top-level VC uses most of the tricks in the book - invalidateLayout(), performBatchUpdates(...), layoutIfNeeded() etc. as well as animating the contentOffset to the correct value. Some manual tracking of the correct page/slide is required.
There's still a slight flicker occasionally at the start of resizing. This may not be an issue with my particular colour-scheme which will be black on dark gray. Bonus points if anyone can suggest how to track this down or alleviate it.
I'm working on a screen that has a UITableView below the screen(thus invisible) initially, but will pop up when user click on a button. I know it's not supposed to be seen on the screen, but just for the sake of design, is there a way to make it visible when I'm working on main.storyboard?
Please see the picture attached below. It's really hard to work on the UITableView when it's beneath the toolbar. How can I force xcode to display them so it's much easier to work on?
You can't, but you have 3 workarounds:
Move it into view (Xcode will complain that it's not in the right position, but it doesn't matter)
On the bottom left on your storyboard, you can open up the view hierarchy, you can find your element there for creating constraints, linking it with your code and everything else you'd want to do.
Increase the visible size of the viewcontroller on your storyboard, trough selecting your viewcontroller on the storyboard, changing Simulated Size to freeform and increasing the values (will not affect size when running).
The easiest way to deal with this is to:
open storyboard
select the viewcontroller you want to edit
open the Size Inspector
Switch from Fixed to Freeform
Type in a larger height (like 1000)
Simplest thing is to manually do what your button does in the app.
In the view hierarchy on the left, select your table view and move it down to the bottom. This will bring it to the front in interface builder.
When you are done, move it back.
I do this all the time and you get used to doing it. Just watch out for accidentally moving it inside another view when you drag it down or up.
I am using ios7 and I am enabled iAds with "self.canDisplayBannerAds"
The iAd basically reshapes the view when it shifts in or when it shifts out. However, When the banner shifts, my view Moves off screen!
In my story board, I put my view outside of the screen. And when the user presses a button, I set its frame property so that it exists inside of the screen. But when the iAd shifts in or out, the View travels back to its original place in the story board.
How can I prevent my view from sliding off the screen?
Update:
I tried simply adding a view programatically. and placed it in the same place as the one I made in Interface Builder. Initially they both move offscreen and on screen when commanded. however, once the iAd shifts in and out, the view I made in interface builder will slide out. The view I made programatically will stay put.
My best guess at this point was Auto layout or something to do with constraints. The IB view was sliding out rather than simply disappearing and It definitely was not using UIView's animation methods. Thus the only other thing that I could think of that could cause animations to happen are constrains. (eg rotating a view with view's that have constriaints will automatically resize in a smooth fashion.
so I took a look at the constraints of the view by calling the constraints method in an NSLog. It printed out a huge list of constraints. I did not add these explicitly, which means they were added automatically.
There may be a connection between these constraints. The question now is, why is the iAD banner triggering the view to basically spring back to its former place?
When the banner shifts in and out, it will layout the subviews. Views without any constraints are given automatic constraints. These constraints place it in the same location as it's place in the xib.
You can try changing the constraints.
For my current project I wanted to use storyboards and autolayout instead of coding everything by hand. It has gone well so far, but my design has a section of the app where there is a tab bar and one of those tab needs to show four views. The design is to swipe between the four and so I thought to use a scroll view. After some trial and error I found that embedding a Container View in the tab allowed me to easily set up a scroll view and put a couple of view inside it, carefully aligning them using positioning to put them side by side so that each page is one child view. I'm not sure how that plays with the autolayout, and in fact I have the problem that the scroll view won't scroll past the first page position. I can drag about 1/3 of the second page into view, but it never brings that page entirely in view.
I've checked the content size and offset and all of the view positions and it all seems correct. And when I use Spark Inspector to change the scroll offset to the position of the second view/page, the app shows the right page/view and I can even scroll back to the first page. I'm a bit perplexed as to what is causing it to not scroll properly. I don't have any code to show as this is all done in storyboards, but I am wondering if anyone has any ideas as to what is wrong?
Alternately, does anyone have an idea for how to use autolayout and storyboards to set up swiping four adjacent views in a tab? I suspect there are ways to do it. I can think of ways to do it in code, but I'm trying to avoid doing it that way.
EDIT: I set the scroll view delegate to the view controller around it and checked the values of contentSize on scrollViewWillBeginDragging and scrollViewDidScroll. It is always set to {0,0} even after I force set it on viewDidLoad. So I tried setting it every time scrollViewWillBeginDragging is called, which seems work, but I don't understand why this happens and it doesn't smell right. Is there some autolayout constraint that might account for this? Does something cause contentSize get set to {0,0} during the layout process?
For lack of any other answer, I'll use my unsatisfying solution as an answer in the hopes that helps someone else in the future:
I set the scroll view delegate to the view controller around it and checked the values of contentSize on scrollViewWillBeginDragging and scrollViewDidScroll. It is always set to {0,0} even after I force set it on viewDidLoad. So I tried setting it every time scrollViewWillBeginDragging is called, which seems to work.
I'd love to know why the contentSize is being reset if anyone finds out!