skip to Main Content

I am trying to create custom segment control using CollectionViewCell. So i can manage text size of the segment control in different devices. But it is not working properly in all the devices.
I have taken one cell inside collection view and used sizeForItemAt delegate method to equally set the cells. There will be only 3 cells. That is shown in the image.

This is the code for sizeForItemAt

func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
    return CGSize(width: (collectionView.frame.width / 3), height: 50.0)
}

Here is the output that i am getting
This is output in iPhone 8. Title is not properly visible

This is output in iPhone 12 Pro Max

so i am trying to find solution using which i can manage text size in all the devices and all cells can take equal spacing as well.

2

Answers


  1. lbl_name.adjustsFontSizeToFitWidth = true
    

    try this

    UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).adjustsFontSizeToFitWidth = true 
    UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).minimumScaleFactor = 6.0
    
    Login or Signup to reply.
  2. Below is one way I can think of doing it but by no means the only way.

    First, I started by setting up a collection view with a horizontal flow layout.

    // Flow layout configuration, mainly to figure out width of the cells
    private func createLayout() -> UICollectionViewFlowLayout {
        
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.minimumLineSpacing = horizontalPadding
        flowLayout.minimumInteritemSpacing = 0
        flowLayout.scrollDirection = .horizontal
        
        // Calculate the available width to divide the segments evenly
        var availableWidth = UIScreen.main.bounds.width
        
        // There will always be segments - 1 gaps, for 3 segments, there will be
        // 2 gaps and for 4 segments there will be 3 gaps etc
        availableWidth -= horizontalPadding * CGFloat(segments.count - 1)
        
        let cellWidth = availableWidth / CGFloat(segments.count)
        
        flowLayout.itemSize = CGSize(width: cellWidth,
                                     height: collectionViewHeight)
        
        return flowLayout
    }
    

    Once I do this, I run into the same problem, that depending on the width of the screen, my text could get cut off.

    UICollectionView UISegmentController UICollectionViewCell custom swift iOS

    So once the width of each segment is determined, we have to calculate the maximum font size to show the complete text for the longest segment and that font size should be applied to all

    In this case, the long segment is Vibration Intensity in terms of string length.

    // Flow layout configuration, mainly to figure out width of the cells
    private func createLayout() -> UICollectionViewFlowLayout {
        
        let flowLayout = UICollectionViewFlowLayout()
        flowLayout.minimumLineSpacing = horizontalPadding
        flowLayout.minimumInteritemSpacing = 0
        flowLayout.scrollDirection = .horizontal
        flowLayout.sectionInset = UIEdgeInsets(top: 0,
                                               left: horizontalPadding,
                                               bottom: 0,
                                               right: horizontalPadding)
        
        // Calculate the available width to divide the segments evenly
        var availableWidth = UIScreen.main.bounds.width
        
        // There will always be segments - 1 gaps, for 3 segments, there will be
        // 2 gaps and for 4 segments there will be 3 gaps etc
        availableWidth -= horizontalPadding * CGFloat(segments.count - 1)
        
        // Remove the insets
        availableWidth -= flowLayout.sectionInset.left + flowLayout.sectionInset.right
        
        let cellWidth = availableWidth / CGFloat(segments.count)
        
        // Add this function
        calculateApproxFontSize(forWidth: cellWidth)
        
        flowLayout.itemSize = CGSize(width: cellWidth,
                                     height: collectionViewHeight)
        
        return flowLayout
    }
    
    private func calculateApproxFontSize(forWidth width: CGFloat) {
        
        // Get the longest segment by length
        if let longestSegmentTitle = segments.max(by: { $1.count > $0.count }) {
            
            let tempLabel = UILabel()
            tempLabel.numberOfLines = 1
            tempLabel.text = longestSegmentTitle
            tempLabel.sizeToFit()
            
            guard var currentFont = tempLabel.font else { return }
            
            var intrinsicSize
                = (longestSegmentTitle as NSString).size(withAttributes: [.font : currentFont])
            
            // Keep looping and reduce the font size till the text
            // fits into the label
            // This could be optimized further using binary search
            // However this should be ok for small strings
            while intrinsicSize.width > width
            {
                currentFont = currentFont.withSize(currentFont.pointSize - 1)
                tempLabel.font = currentFont
                
                intrinsicSize
                    = (longestSegmentTitle as NSString).size(withAttributes: [.font : currentFont])
            }
    
            // Set the font of the current label
            // segmentFontSize is a global var in the VC
            segmentFontSize = currentFont.pointSize
        }
    }
    

    Then set the segmentFontSize in cellForItemAt indexPath

    func collectionView(_ collectionView: UICollectionView,
                        cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
        
        let cell = collectionView
            .dequeueReusableCell(withReuseIdentifier: SegmentCell.reuseIdentifier,
                                 for: indexPath) as! SegmentCell
        
        cell.backgroundColor = .orange
        cell.title.text = segments[indexPath.item]
        
        // Adjust the font
        cell.title.font = cell.title.font.withSize(segmentFontSize)
        
        return cell
    }
    

    This will give you something like this:

    Resize UILabel to fit frame rect in swift iOS using UICollectionView as UISegmentView

    If you found some part difficult to follow, here is the link to the complete code: https://gist.github.com/shawn-frank/03bc06d13f90a54e23e9ea8c6f30a70e

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search