web-dev-qa-db-fra.com

Comment afficher uniquement la bordure inférieure de l'élément sélectionné dans UISegmentedControl?

Je suis extrêmement nouveau dans le développement iOS et j'ai rencontré des problèmes lors de la création d'une application pour un cours.

J'ai créé un contrôle segmenté et sa fonction init (illustrée ci-dessous) est appelée dans la classe de contrôleur de vue contenant le contrôle segmenté. J'ai pu supprimer toutes les bordures et séparateurs du contrôle segmenté de la classe de contrôle segmenté comme suit:

import Foundation
import UIKit

class CashSegmentedControl: UISegmentedControl{

func initUI(){
    removeBorders()
}

func removeBorders(){
    self.tintColor = UIColor.clear

}

This is what my result looks like currently

Je veux qu'il y ait une ligne sous chaque segment QUAND le segment est sélectionné (similaire à instagram)

This is a sample of what I want it to look like

J'ai beaucoup cherché et je suis tombé sur certains messages sur StackOverflow, mais ils semblent concerner des versions plus anciennes de Swift. J'apprécierais vraiment toute aide à ce sujet, et s'il existe une meilleure solution pour personnaliser les bordures (autre que ce que j'ai fait), j'aimerais en savoir plus!

Merci beaucoup :)

13
Richa Netto

Ajoutez le code suivant dans un fichier Swift (commande + N -> Nouveau fichier) séparé):

extension UISegmentedControl{
    func removeBorder(){
        let backgroundImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: self.bounds.size)
        self.setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)

        let deviderImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
        self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
        self.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor.gray], for: .normal)
        self.setTitleTextAttributes([NSForegroundColorAttributeName: UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)], for: .selected)
    }

    func addUnderlineForSelectedSegment(){
        removeBorder()
        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)
        underline.tag = 1
        self.addSubview(underline)
    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        UIView.animate(withDuration: 0.1, animations: {
            underline.frame.Origin.x = underlineFinalXPosition
        })
    }
}

extension UIImage{

    class func getColoredRectImageWith(color: CGColor, andSize size: CGSize) -> UIImage{
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        let graphicsContext = UIGraphicsGetCurrentContext()
        graphicsContext?.setFillColor(color)
        let rectangle = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        graphicsContext?.fill(rectangle)
        let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return rectangleImage!
    }
}

Ensuite, après avoir appelé segmentedControl.addUnderlineForSelectedSegment() à partir de votre méthode viewDidLoad(), et créez une méthode @IBAction Pour le contrôle segmenté comme suit:

@IBAction func segmentedControlDidChange(_ sender: UISegmentedControl){
        segmentedControl.changeUnderlinePosition()
    }

Appelez ensuite segmentedControl.changeUnderlinePosition() depuis cette méthode.

N'oubliez pas de connecter le contrôle segmenté de votre storyboard à la méthode @IBAction Que vous venez de créer.

Très important : N'oubliez pas d'utiliser la disposition automatique dans le storyboard pour déterminer la taille et la position de votre contrôle segmenté.

Voici le résultat:

enter image description here

enter image description here

enter image description here

N'hésitez pas à poser toutes les autres questions que vous pourriez avoir :)

29
fja

Réponse de A.Jam dans Xcode 9.3, Swift version 4.2

import UIKit

extension UISegmentedControl {
    func removeBorder(){
        let backgroundImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: self.bounds.size)
        self.setBackgroundImage(backgroundImage, for: .normal, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .selected, barMetrics: .default)
        self.setBackgroundImage(backgroundImage, for: .highlighted, barMetrics: .default)

        let deviderImage = UIImage.getColoredRectImageWith(color: UIColor.white.cgColor, andSize: CGSize(width: 1.0, height: self.bounds.size.height))
        self.setDividerImage(deviderImage, forLeftSegmentState: .selected, rightSegmentState: .normal, barMetrics: .default)
        self.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor.gray], for: .normal)
        self.setTitleTextAttributes([NSAttributedStringKey.foregroundColor: UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)], for: .selected)
    }

    func addUnderlineForSelectedSegment(){
        removeBorder()
        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor(red: 67/255, green: 129/255, blue: 244/255, alpha: 1.0)
        underline.tag = 1
        self.addSubview(underline)
    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        UIView.animate(withDuration: 0.1, animations: {
            underline.frame.Origin.x = underlineFinalXPosition
        })
    }
}

extension UIImage {

    class func getColoredRectImageWith(color: CGColor, andSize size: CGSize) -> UIImage{
        UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
        let graphicsContext = UIGraphicsGetCurrentContext()
        graphicsContext?.setFillColor(color)
        let rectangle = CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height)
        graphicsContext?.fill(rectangle)
        let rectangleImage = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        return rectangleImage!
    }
}
7
swift2geek

Réponse de A.Jam dans Xcode 10.1, Swift 4.2 version - sans extension UIImage

et un soulignement gris pour tous les segments

extension UISegmentedControl {

  func removeBorder(){

    self.tintColor = UIColor.clear
    self.backgroundColor = UIColor.clear
    self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.orange], for: .selected)
    self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.gray], for: .normal)

  }

  func setupSegment() {
    self.removeBorder()
    let segmentUnderlineWidth: CGFloat = self.bounds.width
    let segmentUnderlineHeight: CGFloat = 2.0
    let segmentUnderlineXPosition = self.bounds.minX
    let segmentUnderLineYPosition = self.bounds.size.height - 1.0
    let segmentUnderlineFrame = CGRect(x: segmentUnderlineXPosition, y: segmentUnderLineYPosition, width: segmentUnderlineWidth, height: segmentUnderlineHeight)
    let segmentUnderline = UIView(frame: segmentUnderlineFrame)
    segmentUnderline.backgroundColor = UIColor.gray

    self.addSubview(segmentUnderline)
    self.addUnderlineForSelectedSegment()

  }
  func addUnderlineForSelectedSegment(){

    let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
    let underlineHeight: CGFloat = 2.0
    let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
    let underLineYPosition = self.bounds.size.height - 1.0
    let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
    let underline = UIView(frame: underlineFrame)
    underline.backgroundColor = UIColor.orange
    underline.tag = 1
    self.addSubview(underline)
  }

  func changeUnderlinePosition(){
    guard let underline = self.viewWithTag(1) else {return}
    let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
    underline.frame.Origin.x = underlineFinalXPosition

  }
}
2
Kobi

vous pouvez l'utiliser

let segmentBottomBorder = CALayer()
segmentBottomBorder?.borderColor = UIColor.blue.cgColor
segmentBottomBorder?.borderWidth = 3
let x = CGFloat(sender.selectedSegmentIndex) * width
let y = sender.frame.size.height - (segmentBottomBorder?.borderWidth)!
let width: CGFloat = sender.frame.size.width/3
segmentBottomBorder?.frame = CGRect(x: x, y: y, width: width, height: (segmentBottomBorder?.borderWidth)!)
sender.layer.addSublayer(segmentBottomBorder!)
2
Gabrail

A.Jams répond pour Swift 5.1 Xcode 11.

import Foundation
extension UISegmentedControl {

    func removeBorder(){

        self.tintColor = UIColor.clear
        self.backgroundColor = UIColor.clear
        self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.stavkrugDarkBlue], for: .selected)
        self.setTitleTextAttributes( [NSAttributedString.Key.foregroundColor : UIColor.gray], for: .normal)
        if #available(iOS 13.0, *) {
            self.selectedSegmentTintColor = UIColor.clear
        } 

    }

    func setupSegment() {
        self.removeBorder()
        let segmentUnderlineWidth: CGFloat = self.bounds.width
        let segmentUnderlineHeight: CGFloat = 2.0
        let segmentUnderlineXPosition = self.bounds.minX
        let segmentUnderLineYPosition = self.bounds.size.height - 1.0
        let segmentUnderlineFrame = CGRect(x: segmentUnderlineXPosition, y: segmentUnderLineYPosition, width: segmentUnderlineWidth, height: segmentUnderlineHeight)
        let segmentUnderline = UIView(frame: segmentUnderlineFrame)
        segmentUnderline.backgroundColor = UIColor.clear

        self.addSubview(segmentUnderline)
        self.addUnderlineForSelectedSegment()
    }

    func addUnderlineForSelectedSegment(){

        let underlineWidth: CGFloat = self.bounds.size.width / CGFloat(self.numberOfSegments)
        let underlineHeight: CGFloat = 2.0
        let underlineXPosition = CGFloat(selectedSegmentIndex * Int(underlineWidth))
        let underLineYPosition = self.bounds.size.height - 1.0
        let underlineFrame = CGRect(x: underlineXPosition, y: underLineYPosition, width: underlineWidth, height: underlineHeight)
        let underline = UIView(frame: underlineFrame)
        underline.backgroundColor = UIColor.stavkrugDarkBlue
        underline.tag = 1
        self.addSubview(underline)


    }

    func changeUnderlinePosition(){
        guard let underline = self.viewWithTag(1) else {return}
        let underlineFinalXPosition = (self.bounds.width / CGFloat(self.numberOfSegments)) * CGFloat(selectedSegmentIndex)
        underline.frame.Origin.x = underlineFinalXPosition

    }
}
0
Dmih