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
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.
CodePudding user response:
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.
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:
If you found some part difficult to follow, here is the link to the complete code: https://gist.github.com/shawn-frank/03bc06d13f90a54e23e9ea8c6f30a70e
CodePudding user response:
lbl_name.adjustsFontSizeToFitWidth = true
try this
UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).adjustsFontSizeToFitWidth = true
UILabel.appearance(whenContainedInInstancesOf: [UISegmentedControl.self]).minimumScaleFactor = 6.0