With the below code I'm able to scroll all cells upto bottom in iPhone 8 but when I run this code in iPhone12 and iPhone11 its not scrolling to bottom.. here showing only 10 cells but the delegate method is calling
here count parameter is page.. each time it displays 10 cells.. like.. when I reach this screen initially it shows 10 cells that's it if there are more products still. it's not scrolling
override func viewDidLoad() {
super.viewDidLoad()
// using framework
let bottomRefreshController = UIRefreshControl()
bottomRefreshController.triggerVerticalOffset = 50
bottomRefreshController.addTarget(self, action: #selector(refresh), for: .valueChanged)
self.collectionView.bottomRefreshControl = bottomRefreshController
self.collectionView.bottomRefreshControl?.tintColor = .clear
}
#objc func refresh() {
serviceCall()
}
fileprivate func allProductsServiceCall(){
let param = ["params": ["category" : catId,
"sub_category" : self.subCatId,
"brands": brands,
"count" : self.currentPageNumber
]] as [String : Any]
APIReqeustManager.sharedInstance.serviceCall(param: param, vc: self, url: getUrl(of: .all_products/*.search*/), header: header) {(responseData) in
if responseData.error != nil{
self.view.makeToast(NSLocalizedString("Something went wrong!", comment: ""))
}else{
if (SearchDataModel(dictionary: responseData.dict as NSDictionary? ?? NSDictionary())?.result?.products ?? [Products]()).count == 0 {
self.view.makeToast("No product to show!".localizeWithLanguage)
return
}
if self.currentPageNumber > 0 {
if (SearchDataModel(dictionary: responseData.dict as NSDictionary? ?? NSDictionary())?.result?.products ?? [Products]()).count > 0 {
self.searchResultData?.result?.products! += SearchDataModel(dictionary: responseData.dict as NSDictionary? ?? NSDictionary())?.result?.products ?? [Products]()
}else {
self.view.makeToast("No more products to show!".localizeWithLanguage)
}
self.productCollectionView.bottomRefreshControl?.endRefreshing()
self.currentPageNumber+=1
DispatchQueue.main.async{
self.productCollectionView.reloadData()
}
}
}
}
// Scroll delegates
// Things needed for expand / collapse
func scrollViewDidScroll(_ scrollView: UIScrollView) {
if (scrollView.contentOffset.y + scrollView.frame.size.height) == scrollView.contentSize.height {
print("at the end")
self.productCollectionView.bounces = true
}else {
print("at the middle, moving down")
}
}
The code is working fine. I mean how I want to show it's coming like that in iPhone8 but, why the scrolling with pagination is not working in iPhone12.
where am I wrong.. please do help... I got stuck here for long
Related
I am looking to improve my memory usage on my app and noticed that using AdMob is adversely affecting my application.
Specifically, with this code:
override func viewDidLoad() {
super.viewDidLoad()
multipleAdsOptions.numberOfAds = 5
let imageAdOptions = GADNativeAdImageAdLoaderOptions()
adLoader = GADAdLoader(adUnitID: "ca-app-pub-xxxxxxxx", rootViewController: self, adTypes: [GADAdLoaderAdType.native], options: [multipleAdsOptions, imageAdOptions])
adLoader.delegate = self
GADMobileAds.sharedInstance().requestConfiguration.testDeviceIdentifiers = [ kGADSimulatorID.description() ]
DispatchQueue.main.async {
self.adLoader.load(GADRequest())
}
..
..
}
I noticed that each time I trigger a viewDidLoad in my application for this code, it increases the memory used by about 10-15mb. If I quickly tap in and out of this screen (which is actually a common action) I can get the MB usage up as high as I want until I stop. I noticed that this line of code is of course the culprit:
self.adLoader.load(GADRequest())
If I comment that out, my app stays at a clean 50mb and even appears to run much better overall.
Am I using AdMob wrong here? should I be loading the ads once and never calling load again in my viewdidload? This is a highly trafficked screen and my test users are reporting app sluggishness until they restart.
This is my main screen (root view controller) and it contains a feed of products and I also have an AdvertisementFeedCell where I load an ad every 5 items in this feed if that makes sense.
class AdvertisementFeedCell: UITableViewCell {
#IBOutlet var adMedia: GADNativeAdView!
#IBOutlet var adHeadline: UILabel!
#IBOutlet var unifiedNativeAdView: GADNativeAdView!
override func awakeFromNib() {
super.awakeFromNib()
}
}
When I populate my table, I insert a new item every 5 spots for an Ad:
let count = self.feedItems.count / 5
//Insert an ad every 5
if (count > 0)
{
for i in 1...count
{
let adFeedItem = FeedItem()
adFeedItem.isAdvertisement = true
self.feedItems.insert(adFeedItem, at: i * 5)
}
}
self.reloadTable()
And when my tableview populates with data, I check if the item is of typeadvertisement
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let feedItem = feedItems[indexPath.row]
if (feedItem.isAdvertisement)
{
let cell:AdvertisementFeedCell = tableView.dequeueReusableCell(withIdentifier: "adCell")! as! AdvertisementFeedCell
if (ads.count > 0 && indexPath.row > 1)
{
let nativeAd = ads[(indexPath.row / 5) - 1]
cell.unifiedNativeAdView.nativeAd = nativeAd
cell.adHeadline.text = nativeAd.headline!
//If there is an icon, use that
if (nativeAd.icon != nil)
{
(cell.unifiedNativeAdView.iconView as? UIImageView)?.image = nativeAd.icon?.image
cell.unifiedNativeAdView.mediaView?.isHidden = true
cell.unifiedNativeAdView.iconView?.isHidden = false
}
else {
//Otherwise, Add Media and hide icon
cell.unifiedNativeAdView.mediaView?.contentMode = UIViewContentMode.scaleToFill
cell.unifiedNativeAdView.mediaView?.mediaContent = nativeAd.mediaContent
cell.unifiedNativeAdView.mediaView?.isHidden = false
cell.unifiedNativeAdView.iconView?.isHidden = true
}
cell.frame = CGRect(x: 0, y: 0, width: 375, height: 108 )
//Add body text if available
(cell.unifiedNativeAdView.bodyView as? UILabel)?.text = nativeAd.body
cell.unifiedNativeAdView.bodyView?.isHidden = nativeAd.body == nil
cell.clipsToBounds = true
cell.unifiedNativeAdView.clipsToBounds = true
}
return cell
}
Thank you!
I'm developing app which contains 3 UICollectionViews in one ViewController.
I can know if UICollectionView is scrolled or not with this code
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let isScrolling: Bool = colView.isDragging || colView.isDecelerating
}
Specifically I want to know if which UICollectionView did scroll in scrollViewDidEndDragging.
But the problem is that I can't know if which collectionview is scrolled before.
Like I want to know if colview2 is scrolling or not.
Is there anybody who knows this solution? I searched solution on Google and Stackoverflow but I don't think there's such a solution for this problem.
Help will be much appreciated.
You can Do it by set tag to CollectionView And Scroll View Delegate method scrollViewDidEndDecelerating Here is the Code :
First Set tag top your CollectionView in your ViewDidLoad method :
firstCollectionView.tag = 1
secondCollectionView.tag = 2
thirdCollectionView.tag = 3
2.Create three Property Observer variable like this upside of your viewDidload:
var whichCollectionViewScrolled = "" {
willSet{
print(newValue)
}
}
var isFirstCollectionViewScrolled = false {
willSet{
print("First CollectionView Scrolled : \(newValue)")
}
}
var isSecondCollectionViewScrolled = false {
willSet{
print("Second CollectionView Scrolled : \(newValue)")
}
}
var isthirdCollectionViewScrolled = false {
willSet{
print("Third CollectionView Scrolled : \(newValue)")
}
}
1.lastly inside of your scrollViewDelegate method cast your scrollview instance and check the tag value like this :
extension ViewController: UIScrollViewDelegate {
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
if let collectionView = scrollView as? UICollectionView {
switch collectionView.tag {
case 1:
whichCollectionViewScrolled = "First"
isFirstCollectionViewScrolled = true
isSecondCollectionViewScrolled = false
isthirdCollectionViewScrolled = false
case 2:
whichCollectionViewScrolled = "second"
isFirstCollectionViewScrolled = false
isSecondCollectionViewScrolled = true
isthirdCollectionViewScrolled = false
case 3:
whichCollectionViewScrolled = "Third"
isFirstCollectionViewScrolled = false
isSecondCollectionViewScrolled = false
isthirdCollectionViewScrolled = true
default:
whichCollectionViewScrolled = "unknown"
}
} else{
print("cant cast")
}
}
Hope it will help You .
Scroll view is the superClass of UICollectionView. Just check that scroll view and collection view you are storing are the same instance.
if collectionView === scrollView {
}
Like this.
Sample project can be found at https://github.com/SRowley90/LargeTitleIssueTestiOS
I am trying to position a segmented control below the Large title in an iOS app. I have a UIToolbar which contains the segmented control inside.
When scrolling up the title and toolbar behave as expected.
When scrolling down the navigation bar is correct, but it doesn't push the UITabBar or the UITableView down, meaning the title goes above the segmented control as can be seen in the images below.
I'm pretty sure it's something to do with the constraints I have set, but I can't figure out what.
The TabBar is fixed to the top, left and right.
The TableView is fixed to the bottom, left and right.
The tableView is fixed vertically to the TabBar
I have the position UITabBarDelegate method set:
func position(for bar: UIBarPositioning) -> UIBarPosition {
return .topAttached
}
Take the delegation of the tableView somewhere:
tableView.delegate = self
Override the scrollViewDidScroll and update toolbar position appearance (since the real position should not change according to have that nice bounce effect.
extension ViewController: UIScrollViewDelegate {
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
var verticalOffset = scrollView.contentOffset.y + defaultNavigationBarHeight
if scrollView.refreshControl?.isRefreshing ?? false {
verticalOffset += 60 // After is refreshing changes its value the toolbar goes 60 points down
print(toolbar.frame.origin.y)
}
if verticalOffset >= 0 {
toolbar.transform = .identity
} else {
toolbar.transform = CGAffineTransform(translationX: 0, y: -verticalOffset)
}
}
}
You can use the following check before applying transformation to make it more reliable and natural to default iOS style:
if #available(iOS 11.0, *) {
guard let navigationController = navigationController else { return }
guard navigationController.navigationBar.prefersLargeTitles else { return }
guard navigationController.navigationItem.largeTitleDisplayMode != .never else { return }
}
Using UIScrollViewDelegate didn't work well with CollectionView and toolbar for me. So, I did:
final class CollectionViewController: UICollectionViewController {
private var observesBag: [NSKeyValueObservation] = []
private let toolbar = UIToolbar()
override func viewDidLoad() {
super.viewDidLoad()
let statusBarHeight = UIApplication.shared.statusBarFrame.height
let navigationBarHeight = navigationController?.navigationBar.frame.height ?? 0
let defaultNavigationBarHeight = statusBarHeight + navigationBarHeight
let observation = navigationController!
.navigationBar
.observe(\.center, options: NSKeyValueObservingOptions.new) { [weak self] navBar, _ in
guard let self = self else { return }
let newNavigatonBarHeight = navBar.frame.height + statusBarHeight
let yTranslantion = newNavigatonBarHeight - defaultNavigationBarHeight
if yTranslantion > 0 {
self.toolbar.transform = CGAffineTransform(
translationX: 0,
y: yTranslantion
)
} else {
self.toolbar.transform = .identity
}
}
observesBag.append(observation)
}
}
Observe the "center" of the navigationBar for changes and then translate the toolbar in the y-axis.
Even though it worked fine when I tried to use this solution with UIRefreshControl and Large Titles it didn't work well.
I set up the refresh control like:
private func setupRefreshControl() {
let refreshControl = UIRefreshControl()
self.webView.scrollView.refreshControl = refreshControl
}
the height of the UINavigationBar is changed after the complete refresh triggers.
I'm currently battling with the keyboard covering some textField issue on my Swift based iPhone app.
This image shows the primary problem (top three images show the problem, bottom three are what it should be doing):
When the textField in the tableViewCell is edited I want to move the whole tableview and navigation bar up. I can do this with the code below (snippets taken from the viewController):
var tableViewShiftedUp = false
...
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! QuantityTableViewCell
let componentLocationQuantity = tableViewData[indexPath.row]
if let locationString = componentLocationQuantity.location.valueForKey("location_name") as? String {
cell.setCellContents(locationString, quantity: componentLocationQuantity.quantity.floatValue)
cell.quantityLabel.delegate = self
}
//get stock level related to current build rate and lead time (good - green, warning - orange, urgent - red)
let status = model.determineStockLevelStatus(component)
cell.setQuantityLabelBackgroundStatusColor(status)
if editingMode == true {
cell.makeQuantityEditable()
}
//add control event to detect text change
cell.quantityLabel.addTarget(self, action: #selector(quantityCellTextChanged(_:)), forControlEvents: UIControlEvents.EditingChanged)
cell.quantityLabel.addTarget(self, action: #selector(quantityCellSelected(_:)), forControlEvents: UIControlEvents.EditingDidBegin)
return cell
}
...
//MARK: - UITextfield delegate
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
if tableViewShiftedUp == true {
moveTable()
}
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
func quantityCellSelected(textField: UITextField){
if tableViewShiftedUp == false {
moveTable()
}
//check table was moved
print(tableView.frame.origin.y)
}
func hideKeyboard(){
self.view.endEditing(true)
if tableViewShiftedUp == true {
moveTable()
}
}
func moveTable(){
if tableViewShiftedUp == true {
animateTableViewMoving(false, moveValue: 250)
animateNavigationBarMoving(false, moveValue: 250)
tableViewShiftedUp = false
} else {
animateTableViewMoving(true, moveValue: 250)
animateNavigationBarMoving(true, moveValue: 250)
tableViewShiftedUp = true
}
}
// moving the tableView
func animateTableViewMoving (up:Bool, moveValue :CGFloat){
let movementDuration:NSTimeInterval = 0.0
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.tableView.frame = CGRectOffset(self.tableView.frame, 0, movement)
UIView.commitAnimations()
}
// moving the navigationBar
func animateNavigationBarMoving (up:Bool, moveValue :CGFloat){
let movementDuration:NSTimeInterval = 0.0
let movement:CGFloat = ( up ? -moveValue : moveValue)
UIView.beginAnimations( "animateView", context: nil)
UIView.setAnimationBeginsFromCurrentState(true)
UIView.setAnimationDuration(movementDuration )
self.midNavigationBar.frame = CGRectOffset(self.midNavigationBar.frame, 0, movement)
UIView.commitAnimations()
}
And it works fine when moving directly to the textField in the tableView. However, when I am already editing a textField outside the tableView the shift up doesn't happen and so the shifting toggle gets out of sync.
I've printed the tableView frame origin:
//check table was moved
print(tableView.frame.origin.y)
so I can see what the tableView is set to and on that first move from textField outside the tableView to textField inside the tableView, this property is what I would expect it to be 134, however it's still at 384 on the screen which it prints the next time it's called.
The problem doesn't occur when moving within cells in the tableView.
It feels like I've got some kind of race condition problem or I'm missing some delegate method being called that is throwing everything off when transitioning from one cell to the next.
Help would be great.
Check out this library: https://github.com/michaeltyson/TPKeyboardAvoiding.
Designed so that it gets the keyboard out of the way of text fields.
When you use autolayout you cannot resize frame directly like you doing, you must work with constraints (you can create IBOutlets for constraints created in IB or add new ones, then send layoutIfNeeded to your view from within an animation block).
You can follow official Apple instructions:
About Auto Layout and Layout Constraints
Animating Layer Content
i'm making an messenger app, like telegram, i would like to implement an infinite scroll, i use JSQMessageViewController for the chat.
this is the code for infinite scroll:
override func scrollViewDidScroll(scrollView: UIScrollView) {
if(scrollView.contentOffset.y >= 0.0 && scrollView.contentOffset.y <= 30.0 && loading == true){
self.loading = false
self.loadMore()
}
}
Then i call this function:
func loadMore() {
//Controllo se nell'array sono presenti altri messaggi
if ((chatList?.messages.count)!-1) != self.messages.count{
//
CATransaction.begin()
CATransaction.setDisableActions(true)
let oldBottomOffset = self.collectionView!.contentSize.height - self.collectionView!.contentOffset.y
UIView.performWithoutAnimation({ () -> Void in
self.collectionView!.performBatchUpdates({ () -> Void in
let idInit = (self.lastMessageId-self.messagePerPage<0) ? 0 : self.lastMessageId-1-self.messagePerPage
let idEnd = self.lastMessageId-1
var indexPaths: [NSIndexPath] = []
for(var i = 0; i<idEnd-idInit; i++){
print("Creo indexpath \(i)")
indexPaths.append(NSIndexPath(forItem: i, inSection: 0))
}
self.collectionView!.insertItemsAtIndexPaths(indexPaths)
let msg = self.chatList?.messages
var i = idEnd
repeat {
let mg = msg![i]
let messagechat:JSQMessage = JSQMessage(senderId: mg.sender, senderDisplayName: "", date:mg.time, text: mg.message)
messagechat.xmppMessageID = mg.messageID
messagechat.status = mg.messageStatus
self.messages.insert(messagechat, atIndex: 0)
self.lastMessageId = i
i--
} while i > idInit
// invalidate layout
self.collectionView!.collectionViewLayout.invalidateLayoutWithContext(JSQMessagesCollectionViewFlowLayoutInvalidationContext())
}, completion: {(finished) in
//scroll back to current position
self.finishReceivingMessageAnimated(false)
self.collectionView!.layoutIfNeeded()
self.collectionView!.contentOffset = CGPointMake(0, self.collectionView!.contentSize.height - oldBottomOffset)
CATransaction.commit()
})
})
}
else {
print("No more messages to load.")
}
}
All work fine but while scrolling the scroll is stopped for 1sec. i suppose is CATransaction that make this and sometimes i see a jump effect, if i add 10 new messages i see for a second the first message of the 10, and then offset is set correctly to the older position.
How can i fix this? i see telegram scrolling is perfect, and there is no jump effects while loading old messages.