Home Blog

ios – UICollectionView freezes after double-tap on a rotated/scaled cell throughout horizontal scrolling


I’ve a horizontally scrolling UICollectionView in Swift.
Inside scrollViewDidScroll, I apply a rotation and scale rework to seen cells to create a card-tilt impact.

The difficulty:
If I double-tap or use two fingers on the gathering view throughout scrolling, the gathering view turns into “frozen” — it stops responding to swipe gestures, and the centered cell stays caught in place till I reload knowledge.

It looks as if making use of a rework in scrollViewDidScroll interferes with the gathering view’s contact dealing with and hit-testing, however I can’t work out easy methods to forestall it with out eradicating the rework impact.

How can I preserve the rework impact and stop the gathering view from freezing after multi-touch or double-tap?

func scrollViewDidScroll(_ scrollView: UIScrollView) {
        guard let collectionView = scrollView as? UICollectionView else { return }
        
        for cell in collectionView.visibleCells {
            // 1. Calculate the cell's horizontal distance from the middle of the display
            let centerX = view.bounds.width / 2
            let cellCenter = collectionView.convert(cell.middle, to: view)
            let distance = centerX - cellCenter.x
            
            // 2. Calculate rotation and scale based mostly on this distance
            // The farther from the middle, the extra it rotates and shrinks.
            let maxDistance = collectionView.bounds.width / 2
            let normalizedDistance = distance / maxDistance // Worth from -1 to 1
            
            let maxAngle = CGFloat.pi / 30 // A refined angle (e.g., 6 levels)
            let angle = maxAngle * normalizedDistance
            
            let minScale: CGFloat = 0.9
            let scale = 1.0 - (abs(normalizedDistance) * (1.0 - minScale))
            
            // 3. Apply the rework
            UIView.animate(withDuration: 0.3, delay: 0, choices: [.beginFromCurrentState, .allowUserInteraction], animations: {
                cell.rework = CGAffineTransform(rotationAngle: angle).scaledBy(x: scale, y: scale)
            }, completion: nil)
            
        }
        // ✅ Set preliminary centered index as soon as after format go
        if !hasSetInitialCenteredIndex {
            hasSetInitialCenteredIndex = true
            DispatchQueue.predominant.asyncAfter(deadline: .now() + 0.05) {
                self.snapToNearestCell()
                self.applyTransformToVisibleCells()
            }
        }
    }
    
    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        
        self.cardsCollectionView.isUserInteractionEnabled = false
        
        isScrolling = true
        
        let velocity = scrollView.panGestureRecognizer.velocity(in: scrollView)
        currentScrollDirection = velocity.x == 0 ? 0 : (velocity.x > 0 ? 1 : -1)
        
    }
    
    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        
        self.cardsCollectionView.isUserInteractionEnabled = true
        
        isScrolling = false
        
        let pageWidth = scrollView.body.measurement.width
        let currentPage = Int((scrollView.contentOffset.x + pageWidth / 2) / pageWidth)
        
        if currentPage == 0 {
            let indexPath = IndexPath(merchandise: infinitePlaceholderArray.rely - 2, part: 0)
            cardsCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
            
            // Delay snapping till format is corrected
            DispatchQueue.predominant.asyncAfter(deadline: .now() + 0.01) {
                self.snapToNearestCell()
                UIView.animate(withDuration: 0.3) {
                    self.applyTransformToVisibleCells()
                }
            }
            return
            
        } else if currentPage == infinitePlaceholderArray.rely - 1 {
            let indexPath = IndexPath(merchandise: 1, part: 0)
            cardsCollectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: false)
            
            // Delay snapping
            DispatchQueue.predominant.asyncAfter(deadline: .now() + 0.01) {
                self.snapToNearestCell()
                UIView.animate(withDuration: 0.3) {
                    self.applyTransformToVisibleCells()
                }
            }
            return
        }
        
        // No wrapping, snap usually
        snapToNearestCell()
        UIView.animate(withDuration: 0.3) {
            self.applyTransformToVisibleCells()
        }
    }
    
    func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
        if !decelerate {
            
            self.cardsCollectionView.isUserInteractionEnabled = true
            
            isScrolling = false
            
            snapToNearestCell()
            UIView.animate(withDuration: 0.3) {
                self.applyTransformToVisibleCells()
            }
        }
    }
    
    func setupInfiniteDataSource() {
        // Ensure you have knowledge to work with
        guard !placeholderArray.isEmpty else { return }
        
        // [Last Item] + [All Original Items] + [First Item]
        infinitePlaceholderArray.append(placeholderArray.final!)
        infinitePlaceholderArray.append(contentsOf: placeholderArray)
        infinitePlaceholderArray.append(placeholderArray.first!)
    }
    
    func applyTransformToVisibleCells() {
        guard let collectionView = cardsCollectionView else { return }
        
        for cell in collectionView.visibleCells {
            let centerX = view.bounds.width / 2
            let cellCenter = collectionView.convert(cell.middle, to: view)
            let distance = centerX - cellCenter.x
            
            let maxDistance = collectionView.bounds.width / 2
            let normalizedDistance = distance / maxDistance
            
            let maxAngle = CGFloat.pi / 30
            let angle = maxAngle * normalizedDistance
            
            let minScale: CGFloat = 0.9
            let scale = 1.0 - (abs(normalizedDistance) * (1.0 - minScale))
            
            cell.rework = CGAffineTransform(rotationAngle: angle).scaledBy(x: scale, y: scale)
        }
    }

    non-public func snapToNearestCell() {
        guard let collectionView = cardsCollectionView else { return }
        
        let centerX = collectionView.bounds.measurement.width / 2 + collectionView.contentOffset.x
        
        var closestIndexPath: IndexPath?
        var closestDistance: CGFloat = .greatestFiniteMagnitude
        
        for cell in collectionView.visibleCells {
            let cellCenterX = cell.middle.x
            let distance = abs(cellCenterX - centerX)
            if distance < closestDistance {
                closestDistance = distance
                closestIndexPath = collectionView.indexPath(for: cell)
            }
        }
        
        if let indexPath = closestIndexPath {
            currentlyCenteredIndexPath = indexPath  // Observe centered cell
            collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
        }
        
    }
    
    non-public func handleInfiniteScrollWrapping(for scrollView: UIScrollView) {
        let pageWidth = scrollView.body.measurement.width
        let currentPage = Int(flooring((scrollView.contentOffset.x - pageWidth / 2) / pageWidth) + 1)
        
        if currentPage == 0 {
            let targetIndexPath = IndexPath(merchandise: infinitePlaceholderArray.rely - 2, part: 0)
            cardsCollectionView.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: false)
        } else if currentPage == infinitePlaceholderArray.rely - 1 {
            let targetIndexPath = IndexPath(merchandise: 1, part: 0)
            cardsCollectionView.scrollToItem(at: targetIndexPath, at: .centeredHorizontally, animated: false)
        }
    }
}

I attempted:

  • Detecting a number of touches in touchesBegan and calling my snapToNearestCell() technique to pressure snapping.

  • Quickly disabling isUserInteractionEnabled on the gathering view throughout snap animations.

  • Forcing scrollViewWillEndDragging and scrollViewDidEndDecelerating logic to run manually after multi-touch.

Anticipated:
The gathering view ought to snap to the closest cell after a multi-touch occasion and stay scrollable as regular.

Precise end result:
After a double-tap or two-finger contact whereas scrolling, the gathering view turns into caught. Scrolling stops working fully, and I’ve to reload the gathering view to revive interplay.

ios – Learn how to ship a communication notification regionally, with out Push server?


I am struggling to ship a communication notification (with the sender’s avatar and his identify). I am making a shopper software for a third-party web site the place sending Push notifications shouldn’t be applied. In consequence, I exploit native notification sending. I discovered little or no info on the Web about these notifications, however the code I discovered would not work for me.

The query is, can communication notifications be despatched solely by Push notifications? If that’s the case, this subject might be thought of closed. If not, why do I’ve an everyday notification despatched, though I turned on the required Capabilities and added Message Intent to Data.plist.

To make clear, I haven’t got a backend server. I am simply asking if notifications with an avatar and speak to identify (communication notifications) work just for Push notifications? I am unable to ship a push, solely a neighborhood notification. BUT I am unable to fashion the native notification below the message fashion with the contact identify and avatar.

func didReceive(
    request: UNNotificationRequest,
    withContentHandler contentHandler: @escaping (UNNotificationContent
    ) -> Void) {
    let deal with = INPersonHandle(worth: "unique-user-id)", sort: .unknown)
    let sender = INPerson(personHandle: deal with,
                                  nameComponents: nil,
                                  displayName: "Title",
                                  picture: nil, // right here you'll be able to present UIImage for consumer avatar
                                  contactIdentifier: nil,
                                  customIdentifier: "unique-user-id")
    
    let intent = INSendMessageIntent(
        recipients: nil,
        outgoingMessageType: .outgoingMessageText,
        content material: "content material of message",
        speakableGroupName: nil,
        conversationIdentifier: "unique-user-id-conv",
        serviceName: nil,
        sender: sender,
        attachments: nil
    )
    
    let interplay = INInteraction(intent: intent, response: nil)
    
    interplay.path = .incoming
    
    interplay.donate(completion: nil)
    
    let content material = request.content material
    
    do {
        let updatedContent = strive content material.updating(from: intent)
        let mutableBestAttemptContent = (updatedContent.mutableCopy() as? UNMutableNotificationContent)!
        mutableBestAttemptContent.threadIdentifier = "Thread-identifier" // use this subject for grouping notifications
        mutableBestAttemptContent.userInfo = request.content material.userInfo
        
        contentHandler(mutableBestAttemptContent)
        
    } catch {
        // Deal with errors that will happen whereas updating content material.
    }
}

An Eco-Pleasant Strategy to Goal Diabetes – NanoApps Medical – Official web site


Researchers have developed an eco-friendly technique to supply silver nanoparticles from the roots of Martynia annua, displaying robust antioxidant and anti-diabetic potential whereas avoiding the poisonous by-products of typical synthesis.

Silver nanoparticles are significantly fashionable in analysis due to their excessive floor area-to-volume ratio, which boosts their reactivity and organic efficiency. Nevertheless, producing these tiny particles is energy-intensive, makes use of poisonous reagents, and generates hazardous waste.

Researchers are turning to crops to discover a much less environmentally compromising path to synthesis. Often called photosynthesis, scientists can harness the pure phytochemicals in crops to scale back silver ions (Ag+) to elemental silver (Ag0) and stabilize the ensuing particles, stopping aggregation. The important thing compounds concerned are terpenoids, flavonoids, saponins, and phenolics, amongst others.

The brand new examine, printed in Nano TransMed, focuses on Martynia annua, a plant with a wealthy phytochemical profile, significantly its roots, because the organic supply for nanoparticle manufacturing.

The researchers first ready an aqueous extract of Martynia annua roots, filtering the liquid rigorously to retain the phytochemicals required for nanoparticle formation. When mixed with a silver nitrate (AgNO3) resolution beneath managed situations, the extract confirmed a visual change in shade from pale yellow to reddish-brown, signalling the onset of floor plasmon resonance, a particular optical property of steel nanoparticles that confirms their synthesis.

The nanoparticles had been characterised utilizing a number of complementary methods: UV/Vis spectroscopy, Fourier-transform infrared spectroscopy (FTIR), scanning electron microscopy (SEM), energy-dispersive X-ray spectroscopy (EDX), and dynamic mild scattering (DLS).

UV/Vis spectroscopy revealed a peak at round 420 nm, a wavelength typical of silver nanoparticles. This offered additional affirmation of the formation of particles exhibiting floor plasmon resonance.

FTIR recognized useful teams on the particle surfaces, akin to hydroxyl and carbonyl moieties, indicating the involvement of plant-derived compounds in capping and stabilization.

SEM pictures confirmed that the nanoparticles had been predominantly polygonal or irregular in form. EDX confirmed the fundamental composition of the nanoparticles, with a majority of silver current at 58.08 %, and hint quantities of different components.

DLS measured the hydrodynamic diameter of the particles at roughly 64 nm, with a polydispersity index of 0.385, suggesting a comparatively uniform measurement distribution appropriate for biomedical functions. Zeta potential evaluation indicated a floor cost of -21.6 mV, reflecting average colloidal stability and a lowered tendency for the nanoparticles to clump collectively in suspension.

Phytochemical evaluation of the foundation extract confirmed a powerful presence of terpenoids, that are believed to facilitate each the discount of silver ions and the long-term stability of the nanoparticles.

Sturdy Bioactivity

The crew additionally assessed the organic properties of the Martynia annua-derived silver nanoparticles to evaluate their therapeutic potential.

Antioxidant assays of the particles demonstrated robust free radical scavenging skill within the DPPH take a look at and substantial ferric lowering antioxidant energy, vital in counteracting oxidative stress. In diabetes, the place oxidative stress performs a major position within the onset and development of the sickness, that is significantly promising.

The nanoparticles inhibited α-amylase in enzymatic research, an enzyme liable for breaking down complicated carbohydrates into easy sugars. By limiting α-amylase exercise, the nanoparticles could assist scale back post-meal spikes in blood glucose ranges. Additional, in cell-based experiments, they had been discovered to boost glucose uptake, indicating potential for enhancing glucose homeostasis in diabetic situations.

A Sustainable Path Ahead

Taken collectively, these outcomes display that the inexperienced synthesis of silver nanoparticles from Martynia annua roots is each possible and efficient, producing steady, well-defined particles with robust antioxidant properties. The mix of beneficial physicochemical attributes, akin to acceptable measurement, form, floor cost, and stability, with measurable organic advantages suggests these plant-derived nanoparticles might contribute to new therapeutic methods.

With rising demand for environmentally accountable nanotechnology, such plant-mediated strategies might assist to strike a steadiness between sustainable manufacturing and high-performance biomedical supplies.

Journal Reference

Abbigeri M.B., et al. (2025). Inexperienced synthesis of silver nanoparticles from Martynia annua: characterization and bioactivity. Nano TransMed, 4, 100070. DOI: 10.1016/j.ntm.2025.100070, https://www.sciencedirect.com/science/article/pii/S2790676025000019

The best way to get a Share Extension engaged on Maui iOS


For the previous few months I have been porting my app from Xamarin to Maui for iOS on Mac. It has been extremely troublesome, largely as a result of instruments failing or not working as marketed.

Proper now I feel I am on the house straight however I am dealing with what appears to be the hardest drawback of all.

My app has a Shared Extension that works in debug mode, however not in launch mode. When working in launch mode (which takes round 20-30 min to compile), after I click on on the my app’s icon within the shared apps panel, the panel goes gray momentarily earlier than returning to its regular color, however the view that I’ve created would not seem.

I am unsure the place to go along with this.

  • Breakpoints will not work as a result of it isn’t debug and even in debug mode they do not work in extensions
  • I’ve double checked provisioning and it is all right (precisely the identical as what I used to be utilizing for Xamarin)
  • I’ve checked my entitlements, share teams and bundle names, all are right
  • I’ve tried setting debug remarks to the clipboard, however SharedExtensions do not assist clipboard entry

Provided that it really works in debug and works in manufacturing in Xamarin, I am assuming it is a configuration drawback fairly than a code drawback. As such the next recordsdata could also be accountable (?):

Right here is the Major app csproj:



    
        
        net9.0-ios 
   
        Exe
        MyApp
        true
        true
        allow
        allow

        
        MyApp

        
        com.MyApp.MyApp

        
        1.0
        1

        12.2
        13.1
        21.0
        10.0.17763.0
        10.0.17763.0
        6.5
        
        
    

    
        
            true
            false
        
    

    ...
    ...
    ...
    ...
    ...


Right here is the ShareExtension csproj:


  
    net9.0-ios
    Library
    com.MyCompany.MyApp.ShareExtension
    allow
    true
    13.0

        true
    
    full
  

  
      true
      false
  

  
    Apple Improvement: Created by way of API (XXXXXXXXXX)
    VS: com.MyCompany.MyApp.ShareExtension Improvement
  


Right here is the Major app information.plist:





    UIDeviceFamily
    
        1
        2
    
    UISupportedInterfaceOrientations
    
        UIInterfaceOrientationPortrait
        UIInterfaceOrientationLandscapeLeft
        UIInterfaceOrientationLandscapeRight
    
    UISupportedInterfaceOrientations~ipad
    
        UIInterfaceOrientationPortrait
        UIInterfaceOrientationPortraitUpsideDown
        UIInterfaceOrientationLandscapeLeft
        UIInterfaceOrientationLandscapeRight
    
    MinimumOSVersion
    12.2
    CFBundleDisplayName
    MyApp
    CFBundleIdentifier
    com.MyCompany.MyApp
    CFBundleName
    MyApp
    XSAppIconAssets
    Belongings.xcassets/appicon.appiconset
    UILaunchStoryboardName
    LaunchScreen
    UIViewControllerBasedStatusBarAppearance
    
    NSExtensionPointIdentifier
    com.apple.share-services
    CFBundleURLTypes
    
        
            CFBundleURLName
            com.MyCompany.MyApp
            CFBundleURLSchemes
            
                shareto
            
            CFBundleTypeRole
            None
        
    
    NSAppleMusicUsageDescription
    Choose audio file for sheet
    NSMicrophoneUsageDescription
    Report audio loop
    UIBackgroundModes
    
        audio
    
    CFBundleShortVersionString
    1.0.001
    CFBundleVersion
    1
    ITSAppUsesNonExemptEncryption
    
    UIFileSharingEnabled
    


Right here is the Share Extension information.plist:




    
        CFBundleDevelopmentRegion
        en
        CFBundleDisplayName
        MyApp
        CFBundleIdentifier
        com.MyCompany.MyApp.ShareExtension
        CFBundleInfoDictionaryVersion
        6.0
        CFBundleName
        com.MyCompany.MyApp.ShareExtension
        CFBundlePackageType
        XPC!
        CFBundleSignature
        ????
        MinimumOSVersion
        13.0
        NSExtension
        
            NSExtensionAttributes
            
                NSExtensionActivationRule
                
                    NSExtensionActivationSupportsFileWithMaxCount
                    1
                    NSExtensionActivationSupportsImageWithMaxCount
                    1
                    NSExtensionActivationSupportsMovieWithMaxCount
                    0
                    NSExtensionActivationSupportsText
                    
                    NSExtensionActivationSupportsWebURLWithMaxCount
                    1
                
            
            NSExtensionPrincipalClass
            CodeBasedViewController
            NSExtensionPointIdentifier
            com.apple.share-services
        
        CFBundleURLTypes
        
            
                CFBundleURLName
                URL Kind 1
                CFBundleTypeRole
                Editor
            
        
        CFBundleShortVersionString
        1.0.001
        ITSAppUsesNonExemptEncryption
        
        CFBundleVersion
        001
    

What can I do to debug this? It is an extremely troublesome drawback to resolve, particularly given the 20-30 min compile occasions which suggests I can solely check out 2 or 3 completely different trouble-shooting makes an attempt per hour, making it exhausting to make significant progress.

Any concepts could be a lot appreciated.

android – Points with displaying pop up for location permission in iOS


In my Flutter challenge, I’m utilizing a lib permission_handler for permissions.

I’m going through points with displaying popup dialogue whereas requesting permission for location. My code is as follows:

Future _requestLocationPermission() async {
    // Test if location providers are enabled
    bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
    if (!serviceEnabled) {
      // Location providers aren't enabled
      return;
    }
    
    PermissionStatus standing = await Permission.location.request();
    // Step 2: Test permission standing
    //PermissionStatus standing = await Permission.location.standing;
    if (standing.isGranted) {
      print("Permission granted");
      _getCurrentLocation();
    } else if (standing.isDenied) {
      print("Permission denied");
    } else if (standing.isPermanentlyDenied) {
      print("Permission Completely denied");
    }
  }

And because it was steered as prerequisite like, some traces must be included in information.plist file that are as:

NSLocationWhenInUseUsageDescription
This app wants location entry whereas utilizing the app.

NSLocationAlwaysUsageDescription
This app wants location entry always.

NSLocationAlwaysAndWhenInUseUsageDescription
This app wants location entry at all times and when in use.

UIBackgroundModes

  location

And in xcode, Runner -> signing and capabilities -> I checked Location Updates underneath Background Modes

After doing these above issues, I used to be unable to open permission popup for location.

I analysis and obtained an answer on stack overflow at https://stackoverflow.com/a/68600411/5034193
which tells that, Permissions on iOS are disabled by default, and you’ve got the set the proper GCC_PREPROCESSOR_DEFINITIONS in you Podfile.

It steered traces for nearly all permissions however I picked solely the road for location which was ‘PERMISSION_LOCATION=1’, and didn’t write for different permissions. As a result of different permissions like permission for storage, notification and so on.. are working tremendous.

After doing this it begins opening popup for location permission.

However the query is why it’s not required to switch the Podfile for different permissions like storage and notification and so on however Location.

Does It require any settings in apple developer app retailer join or any the place to keep away from modifying the Podfile?

And another doubt, aside from this as:
when it opens dialogue and on Do not enable it goes into standing.isPermanentlyDenied however within the case of Android It goes into standing.isDenied
Why it’s not uniform for each, Android and iOS?