I've been trying to scroll UICollectionView with horizontal scroll, to the next page when isPagingEnabled property was set as true. I've been working on it for couple of days and I've made a lot of research, but I couldn't find any case like mine. If you already had this problem and if you already found a solution for it, it would be great sharing your solution way with me. Here is my current case;
func sampleTest() {
let collectionView = app.collectionViews[.sampleCollectionView]
collectionView.waitUntil(.exists)
let totalPageCount = collectionView.cells.count
guard totalPageCount > 0 else {
XCTFail("No pages could find in collection to take snapshot.")
return
}
for currentPage in 1...totalPageCount {
snapshot("Page\(currentPage)")
collectionView.swipeLeft()
}
}
Here, swipeLeft() method of XCUIElement is not working as expected in my case. When I call the method, it is not moving to the next page. It swipes a little bit and turn back due to isPagingEnabled = true statement.
In addition, there is another problem that collectionView.cells.count is calculated wrong. It always returns 1. I assume that the reason of the problem is about reusability. Because the other cells has not dequeued yet. Or collectionView.cells.count is not working as I guess?
Related
I have a class that subclasses UIPageViewController which contains 4 view controllers.
I am trying to figure out why the below code randomly stops the UIPageViewController from scrolling as I try to stop the bounce effect on the first and on the last controller on the UIPageViewController?
If you keep track of your currentIndex then the below should be sufficient but its a little buggy because there is a random scenario where it stops scrolling altogether and gets stuck on a random view controller.
I think the scrollView.bounces is a little buggy, perhaps I am missing something because most of the time it works fine, if anyone is able to have a solution based on the below it would be great please.
public func scrollViewDidScroll(_ scrollView: UIScrollView) {
scrollView.bounces = currentIndex == 0 ||
currentIndex == controllers.count - 1
? false
: true
}
I managed to get this issue working but the problem I have is trying to figure out how I can scroll half way into the next controller.
UIPageViewController - Detect scrolling halfway into the next view controller to change button color?
Since the iOS update to 13 my app is unfortunately behaving bad.
The containers height is controlled by the UISegmentControl element.
The hook works fine and the correct index is set.
There are 3 subcontainers:
profileDataContainerView
addValueContainerView
checkInHistoryContainerView
Unfortunately the containers within the main scrollview dont change their height.
Does anyone know the solution for this issue?
#IBAction func showComponent(_ sender: UISegmentedControl) {
showComponent(index: sender.selectedSegmentIndex)
}
private func showComponent(index: Int) {
currentContainer?.bounds.size.height = 0
currentSegmentedControllerIndex = index
switch index {
case 0:
profileDataContainerView.bounds.size.height = 308
currentContainer = profileDataContainerView
case 1:
addValueContainerView.bounds.size.height = 294
currentContainer = addValueContainerView
case 2:
if let tableView = checkInHistoryContainerView.subviews.last as? UITableView {
checkInHistoryContainerView.bounds.size.height = tableView.contentSize.height
}
currentContainer = checkInHistoryContainerView
default:
break
}
updateContentHeight()
}
You can use yourview.layer.bounds instead of yourview.bounds.
It's because it's a struct.
this will work
myView.frame = Frame(....)
this will not
myView.bounds.height = someNumber
It is hard to say for sure why your app is not working with just the code provided, but you can keep in mind a few things.
A scroll view size is adjusted automatically by its content. Try adjusting the scrollview itself, see this link: how to set scrollview content size in swift 3.0
Adjust the content size and then reload the view afterwards. Try to make sure that your app is loading all of your views after you set the new size. This link may help you with changing frame sizes: Change frame programmatically with auto layout
In general storyboards are a disaster for things like this if you aren't careful. Storyboards tend to give the illusion of success when in fact the constraints are wrong.
One final thing to check (a bug I found within my code) is that segues have changed substantially, so make sure that your segues are the correct type and you are presenting in the way you want to. Here is a link on some of the presenting changes in iOS 13:
https://medium.com/#hacknicity/view-controller-presentation-changes-in-ios-13-ac8c901ebc4e
Edit: Chris replied mentioning that you are using a struct, if thats the case then they are correct in that you are not properly changing the value.
This is a problem stumping me and my team at work.
We have a header view we are using in one section that is not always drawing. It's inconsistent, though appears to be slightly more frequent on fresh installs.
I will first preempt by stating we are not registering it to the table view and thus not actually dequeueing it. I tried that but the boss says since we are never actually re-using it he is adamant against doing so. Therefore we have it like this:
class DashboardViewController {
...
var trendsHeader: TrendsHeader?
...
override func viewDidLoad() {
super.viewDidLoad()
setupTrendsHeader()
....
}
...
func setupTrendsHeader() {
trendsHeader = Bundle(for: TrendsHeader.self).loadNibNamed(TrendsHeader.identifier, owner: view, options: nil)?.first as? TrendsHeader
trendsHeader?.delegate = self
trendsHeader?.datasource = self
trendsHeader?.leftDropdown.selectRowWithAction(at: 0)
}
...
// in heightForHeaderInSection
return TrendsHeader.cellHeight //Height is returned properly, empty space is of the right height
...
// in viewForHeaderInSection
return trendsHeader ?? UIView()//Should only return UIView if trendsHeader is nil
}
I have tried modifying viewForHeaderInSection as such:
//in viewForHeaderInSection
if trendsHeader == nil {
setupTrendsHeader()//Breakpoint inserted here
}
return = trendsHeader ?? UIView()
And with a breakpoint in the if statement so I should know if the trendsHeader is nil. It doesn't hit the breakpoint but still doesn't draw that header. If I wait anywhere from 5–30 seconds the header will show up, or if I scroll down the header will show up once it redraws the section. But I need it to show up initially as well, which it still does in most runs of the app but sometimes just doesn't. Honestly the most frustrating part is the inconsistency.
Any insight as to why this is occurring and/or a resolution that doesn't involve convincing my boss to register a view for re-use that isn't going to be re-used?
We found it. Long story short, the section in question is tied to 2 service calls, one for each option on the header's drop down menu. Those services had delegates who reloaded the section upon the callback functions, and they were getting them in quick succession causing the glitch.
We replaced the reload section code with reload rows code instead, so the header does not get refreshed.
I have a subclass of UICollectionViewController that is nested inside a UINavigationController. The collection contains several cells (currently, 3) and each cell is as big as the full screen.
When the whole thing is shown, the collection view initally scrolls to a specific cell (which works flawlessly for each cell):
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let path = currentlyPresentedPhotoCellIndexPath { // this is set in the beginning
collectionView?.scrollToItemAtIndexPath(path, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: false)
}
}
However, the collection view refuses to scroll horizontally, hereafter, as if the user interaction was disabled. I am not sure what is happening, but this is what I have checked so far:
user interaction is enabled for the collection view
the next cell (right or left, depending on the scroll direction) is requested correctly which I found out by inspecting collectionView:cellForItemAtIndexPath:
the requested imagePath is the right one
scrollToItemAtIndexPath... does not work either if I try to trigger a scroll programmatically after everything has been loaded (nothing happens)
scrollRectToVisible... does neither
setting collectionView?.contentInset = UIEdgeInsetsZero before the programmatic scroll attempts take place does not change anything
the content size of the collection view is 3072x768 (= 3 screens, i.e. 3 cells)
Which bullet points are missing, here?
Although the post did not precisely tackle the root of my problem it forced me to ponder the code that I posted. If you look at it you will see that it basically says: Whenever the views need to be layouted, scroll to the cell at position currentlyPresentedPhotoCellIndexPath. However, and this you cannot see without any context, this variable is only set once, when the whole controller is being initialized. Thus, when you try to scroll, the layout changes, the controller then jumps back to the initial cell and it looks like nothing happens, at all.
To change this, you just have to enforce a single scroll, e.g. by doing this:
override func viewDidLayoutSubviews() {
super.viewDidLayoutSubviews()
if let path = currentlyPresentedPhotoCellIndexPath { // only once possible
collectionView?.scrollToItemAtIndexPath(path, atScrollPosition: UICollectionViewScrollPosition.CenteredHorizontally, animated: false)
currentlyPresentedPhotoCellIndexPath = nil // because of this line
// "initiallyPresentedPhotoCellIndexPath" would probably a better name
}
}
A big thanks to Mr.T!
It seems like Apple's new feature of auto-flip interface on RTL languages cause problems when using UICollectionView.
I used constraints of type Trailing/Leading for the collection view and they switched their values, as they should, on RTL language.
The problem is that the data actually presented is of the last indexPath in the collection's data source but the UIScrollView.contentOffset.x of the first cell is 0.
A proper behaviour would have been one of the following:
Displaying the first indexPath correctly and switching the direction of the scroll (to the right) - Best option
Not flipping the UI/Constraints so the presented-data / indexPath / scrollView.contentOffset.x will be synchronised - Option that disabling the RTL support.
Presenting cell and data of the last indexPath but fixing the scrollView.contentOffset.x to represent the last cell position also.
I guess Apple might fix it sometime in the future but meanwhile we'll have to use workarounds like reversing array and/or scrolling to the last object.
I was in a similar situation and found a solution for this. If you are using swift, add the following snippet to your project, and it will make sure that the bounds.origin always follows leading edge of the collection view.
extension UICollectionViewFlowLayout {
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
If you are using Objective-C, just subclass the UICollectionViewLayout class, and override flipsHorizontallyInOppositeLayoutDirection, and return true. Use this subclass as the layout object of your collection view.
I am late but if you don't want to create an extension because it will affect all the collection View in our app. Simply create your own custom class ie.
class CustomLayoutForLocalization : UICollectionViewFlowLayout{
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return true
}
}
To use this class:
// your way of deciding on whether you need to apply this layout may vary depending on use of other tools like LanguageManager_iOS to handle multi-language support
if myCollectionView.effectiveUserInterfaceLayoutDirection == .rightToLeft {
let customLayout = CustomLayoutForRTL()
// if your elements are variable size use the following line
customLayout.estimatedItemSize = UICollectionViewFlowLayout.automaticSize
// if you want horizontal scroll (single line)
customLayout.scrollDirection = .horizontal
myCollectionView.collectionViewLayout = customLayout
}
There is one common solution for that problem that works for me, follow below steps to overcome that problem,
Give the auto layout constraint as per your requirement and then from attribute inspector change the semantic control property of the collection view to Force right-to-left from the storyboard.
Then open storyboard as source code and find for the “leading” attributes of your relevant collection view and replace that with the “left” and same for the “trailing” replace that with the “right”. Now you almost done.
now that will give you result as per your requirement.
import UIKit
extension UICollectionViewFlowLayout {
open override var flipsHorizontallyInOppositeLayoutDirection: Bool {
return UIApplication.shared.userInterfaceLayoutDirection == UIUserInterfaceLayoutDirection.rightToLeft
}
not pretty though simple math does the trick. (for horizontal collectionview)
- (void)switchSemanticDirection:(UISwitch*)sender {
//TEST switch the semantic direction between LTR and RTL.
if (sender.isOn) {
UIView.appearance.semanticContentAttribute = UISemanticContentAttributeForceLeftToRight;
} else {
UIView.appearance.semanticContentAttribute = UISemanticContentAttributeForceRightToLeft;
}
[self.myContent removeFromSuperview];
[self.view addSubview:self.myContent];
//reload your collection view to apply RTL setting programmatically
[self.list reloadData];
//position your content into the right offset after flipped RTL
self.list.contentOffset = CGPointMake(self.list.contentSize.width - self.list.contentOffset.x - self.list.bounds.size.width,
self.list.contentOffset.y);
}