Swift UI Snippets

Scroll View, Image View, Keyboard, UIControl extension, Gradient View, Shadow view, Custom Textfield…

UILabel

Scroll View

import UIKit
class ViewController: UIViewController, UIScrollViewDelegate {
    
    @IBOutlet weak var scrollView: UIScrollView!
    @IBOutlet weak var imageView: UIImageView!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        scrollView.delegate = self
        scrollView.contentInsetAdjustmentBehavior = .never
        imageView.frame.size = (imageView.image?.size)!
        setZoomParametersForSize(scrollView.bounds.size)
        recenterImage()
    }
    
    override func viewWillLayoutSubviews() {
        print("viewWillLayoutSubviews")
        setZoomParametersForSize(scrollView.bounds.size)
        recenterImage()
    }
    
    func setZoomParametersForSize(_ scrollViewSize: CGSize) {
        let imageSize = imageView.bounds.size
        let widthScale = scrollViewSize.width / imageSize.width
        let heightScale = scrollViewSize.height / imageSize.height
        let minScale = min(widthScale,heightScale)
        scrollView.minimumZoomScale = minScale
        scrollView.maximumZoomScale = 2.0
        scrollView.zoomScale = minScale // 처음 시작
    }
    
    func recenterImage() {
        let scrollviewSize = scrollView.bounds.size
        let imageSize = imageView.frame.size
        let horizontalSpace = imageSize.width < scrollviewSize.width ? (scrollviewSize.width - imageSize.width) / 2 : 0
        let verticalSpace = imageSize.height < scrollviewSize.height ? (scrollviewSize.height - imageSize.height) / 2 : 0
        scrollView.contentInset = UIEdgeInsets(top: verticalSpace, left: horizontalSpace, bottom: 0, right: 0)
    }
    
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
    
}

Paging Controller

func scrollViewDidScroll(_ scrollView: UIScrollView) {
    let pageWidth = scrollView.bounds.width
    let pageFraction = scrollView.contentOffset.x / pageWidth
    pageControl.currentPage = Int(round(pageFraction))
}

Image View

override func viewDidLoad() {
    super.viewDidLoad()

    // animate the wings
    var animationFrames = [UIImage]()
    for i in 0...3 {
      if let image = UIImage(named: "Bird\(i)") {
        animationFrames.append(image)
      }
    }

    felipeImageView.animationImages = animationFrames
    felipeImageView.animationDuration = 0.4
    felipeImageView.startAnimating()
}
  

Keyboard

class ViewController: UIViewController {

  @IBOutlet weak var scrollView: UIScrollView!
  
  override func viewDidLoad() {
    super.viewDidLoad()
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(notification:)), name: .UIKeyboardWillHide, object: nil)
    NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(notification:)), name: .UIKeyboardWillShow, object: nil)
  }
  
  deinit {
    NotificationCenter.default.removeObserver(self)
  }
  
  @objc func keyboardWillShow(notification: NSNotification) {
    adjustInsetForKeyboardShow(show: true, notification: notification)
  }
  
  @objc func keyboardWillHide(notification: NSNotification) {
    adjustInsetForKeyboardShow(show: false, notification: notification)
  }
  
  func adjustInsetForKeyboardShow(show: Bool, notification: NSNotification) {
    let userInfo = notification.userInfo ?? [:]
    let keyboardFrame = (userInfo[UIKeyboardFrameBeginUserInfoKey] as! NSValue).cgRectValue
    let adjustment = keyboardFrame.height * (show ? 1 : -1) + 20
    scrollView.contentInset.bottom += adjustment
    scrollView.scrollIndicatorInsets.bottom += adjustment

  }

}

Gradient View

import UIKit

class GradientView: UIView {
  
  lazy fileprivate var gradientLayer: CAGradientLayer = {
    let layer = CAGradientLayer()
    layer.colors = [UIColor.clear.cgColor, UIColor(white: 0.0, alpha: 0.75).cgColor]
    layer.locations = [NSNumber(value: 0.0 as Float), NSNumber(value: 1.0 as Float)]
    return layer
    }()
  
  override func awakeFromNib() {
    super.awakeFromNib()
    backgroundColor = UIColor.clear
    layer.addSublayer(gradientLayer)
  }
  
  override func layoutSubviews() {
		super.layoutSubviews()
		CATransaction.begin()
		CATransaction.setDisableActions(true)
		gradientLayer.frame = bounds
		CATransaction.commit()
  }
  
}

Shadow View

class ShadowView: UIView {
    // MARK : - StoryBoard Initializer
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.layer.shadowColor = UIColor.black.withAlphaComponent(0.5).cgColor
        self.layer.shadowOffset = CGSize(width: 2, height: 2)
        self.layer.shadowOpacity = 0.5
    }
}

Round Button

import UIKit
class RoundButton: UIButton {
    required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)
        self.layer.cornerRadius = 16
        self.layer.borderColor = self.tintColor.cgColor
        self.layer.borderWidth = 1
    }
}

Custom TextField

import UIKit

class CustomTextField: UITextField {

    override func leftViewRect(forBounds bounds: CGRect) -> CGRect {
        let leftViewWidth = self.bounds.size.height / 3
        let resultRect = CGRect(x: 10, y: self.bounds.height/2-leftViewWidth/2, width: leftViewWidth, height: leftViewWidth)
        return resultRect
    }
    
    override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
        let edgeInsetRect = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(0, 10, 0, 0))
        // 컨텐트 안쪽으로 들어가는 패딩값 (+는 패딩값 up / -는 마진값 up)
        return edgeInsetRect
    }
    
    /*
    override func editingRect(forBounds bounds: CGRect) -> CGRect {
        return UIEdgeInsetsInsetRect(
            self.bounds,
            UIEdgeInsetsMake(0, self.bounds.size.height/2+10, 0, 0))
    }
    
    override func textRect(forBounds bounds: CGRect) -> CGRect {
        let edgeInsetRect = UIEdgeInsetsInsetRect(self.bounds, UIEdgeInsetsMake(0, self.bounds.size.height/2+10, 0, 0))
        return edgeInsetRect
    }
    */

    func configureAttributedString(
        string: String,
        range: NSRange,
        stringColor: UIColor
    ){
        let attributedString = NSMutableAttributedString(string: string)
        attributedString.addAttribute(
            NSAttributedStringKey.foregroundColor,
            value: stringColor,
            range: range)
        // NSMutableAttributedString는 class이기 때문에 let으로 선언했으나 내부 속성 바꿔도 OK
        self.attributedPlaceholder = attributedString
    }
    
}

imagePadding (computed)

var imagePadding: CGFloat {
  get {
    return image?.alignmentRectInsets.top ?? 0
  }
  set {
    image = image?.withAlignmentRectInsets(
      UIEdgeInsets(
        top: -newValue,
        left: -newValue,
        bottom: -newValue,
        right: -newValue
		) 
	)
	} 
}

borderWidth (computed)

var borderWidth: CGFloat {
    get {
      return layer.borderWidth
    }
    set {
      layoutMargins = UIEdgeInsets(
        top: newValue*2,
        left: newValue,
        bottom: newValue/2,
        right: newValue
      )
      layer.borderWidth = newValue
    }
}

Extension :: UIControl

override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesBegan(touches, with: event)
    animate(isPressed: true)
}
    
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesCancelled(touches, with: event)
    animate(isPressed: false)
}
    
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
    super.touchesEnded(touches, with: event)
    animate(isPressed: false)
}


private func animate(isPressed: Bool) {
    
    let (duration, backgroundColor, isHidden) =
    
    isPressed ? ( 0.5, pressedBackgroundColor, true) : ( 0.1, unpressedBackgroundColor, false)
    
    UIView.animate(withDuration: duration) {
        self.backgroundColor = backgroundColor
        self.label.isHidden = isHidden
    }
    
}