Skip to content

Instantly share code, notes, and snippets.

@byJeevan
Forked from bobbyali/Swift iOS Recipes
Last active April 6, 2024 14:05
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save byJeevan/9b72128ed97a1dd80a389d683739b6f4 to your computer and use it in GitHub Desktop.
Save byJeevan/9b72128ed97a1dd80a389d683739b6f4 to your computer and use it in GitHub Desktop.
Swift iOS Code Recipes
Swift iOS Recipes
==================
> https://github.com/JohnSundell/SwiftTips
** GUI Layout **
//Avoid Table view having Textfield covered by Keyboard
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillShow), name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(self.keyboardWillHide), name: .UIKeyboardWillHide, object: nil)
view.endEditing(true)
}
@objc func keyboardWillShow(_ notification: Notification) {
let keyboardSize: CGSize? = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as AnyObject).cgRectValue?.size
let animationDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]) as? CGFloat
UIView.animate(withDuration: TimeInterval(animationDuration!), animations: {() -> Void in
self.tableView.contentInset = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize?.height)!, 0.0)
self.tableView.scrollIndicatorInsets = UIEdgeInsetsMake(0.0, 0.0, (keyboardSize?.height)!, 0.0)
})
}
@objc func keyboardWillHide(_ notification: Notification) {
let animationDuration = (notification.userInfo?[UIKeyboardAnimationDurationUserInfoKey]) as? CGFloat
UIView.animate(withDuration: TimeInterval(animationDuration!), animations: {() -> Void in
self.tableView.contentInset = UIEdgeInsets.zero
self.tableView.scrollIndicatorInsets = UIEdgeInsets.zero
})
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillShow, object: nil)
NotificationCenter.default.removeObserver(self, name: .UIKeyboardWillHide, object: nil)
}
///End///
//DismissView - when dialog kind of presented and tapped out side of box. Target view usually the container/parent view of dialog.
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first, !<target view>.bounds.contains(touch.location(in: <target view>)) {
//do dismissal call
}
}
//Keyboard shows over textfield when dialog appears
//in viewDidLoad
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow), name: NSNotification.Name.UIKeyboardWillShow, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide), name: NSNotification.Name.UIKeyboardWillHide, object: nil)
//Add these selectors
@objc func keyboardWillShow(notification: NSNotification) {
if !isKeyboardAppear {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y == 0{
self.view.frame.origin.y -= keyboardSize.height
}
}
isKeyboardAppear = true
}
}
@objc func keyboardWillHide(notification: NSNotification) {
if isKeyboardAppear {
if let keyboardSize = (notification.userInfo?[UIKeyboardFrameEndUserInfoKey] as? NSValue)?.cgRectValue {
if self.view.frame.origin.y != 0{
self.view.frame.origin.y += keyboardSize.height
}
}
isKeyboardAppear = false
}
}
//Alerts
var alert = UIAlertController(title: "Alert", message: "Message", preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Click", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alert, animated: true, completion: nil)
//Buttons on navigation
let newButton = UIBarButtonItem(barButtonSystemItem: .Edit, target: self, action: "doSomething:")
self.navigationItem.leftBarButtonItem = newButton
### Print as JSON by given Dictionary or [AnyHashable:Any]
```
if let theJSONData = try? JSONSerialization.data(
withJSONObject: <Your Dictionary>,
options: [.prettyPrinted]) {
let theJSONText = String(data: theJSONData,
encoding: .ascii)
print("JSON string = \(theJSONText!)")
}
```
### for Objective-C :
```
NSError *jerror;
NSData *jsonData = [NSJSONSerialization dataWithJSONObject:responseData
options:NSJSONWritingPrettyPrinted // Pass 0 if you don't care about the readability of the generated string
error:&jerror];
if (! jsonData) {
NSLog(@"Got an error: %@", jerror);
} else {
NSString *jsonString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
NSLog(@"JSON >> %@", jsonString);
}
```
// Table View inside Table View Cell issue
// - Outer cell will not expand as per inner table view height.
// PS: Make InnerTableView scrolling false.
class InnerTableView: UITableView {
override var intrinsicContentSize: CGSize {
self.layoutIfNeeded()
return self.contentSize
}
override var contentSize: CGSize {
didSet{
self.invalidateIntrinsicContentSize()
}
}
}
// Button Quick for test
let button = UIButton.buttonWithType(UIButtonType.System) as UIButton
button.frame = CGRectMake(100, 100, 100, 50)
button.backgroundColor = UIColor.greenColor()
button.setTitle("Test Button", forState: UIControlState.Normal)
button.addTarget(self, action: "buttonAction:", forControlEvents: UIControlEvents.TouchUpInside)
button.layer.cornerRadius = 5
button.layer.borderWidth = 1
button.layer.borderColor = UIColor.blackColor().CGColor
self.view.addSubview(button)
func buttonAction(sender:UIButton!) {
//do something
}
// Quick Label creation for test
newLabel = UILabel(frame: CGRect(x: 20, y: 10, width: 300, height: 200))
newLabel.text = "Tap and hold for settings"
newLabel.textColor = UIColor.whiteColor()
newLabel.textAlignment = NSTextAlignment.Center
newLabel.font = UIFont(name: "HelveticaNeue", size: CGFloat(17))
// Slider
var slider = UISlider(frame: CGRectMake(0, 0, 300, 200))
slider.addTarget(self, action: "changeSomething:", forControlEvents: UIControlEvents.ValueChanged)
slider.backgroundColor = UIColor.blackColor()
slider.minimumValue = 0.0
slider.maximumValue = 1.0
slider.continuous = true
slider.value = 0.5
self.view.addSubview(slider)
func changeSomething(sender:UISlider!) {
self.someValue = sender.value
}
//Dimensions
var screenSize: CGRect = UIScreen.mainScreen().bounds
//Collection View
let layout: UICollectionViewFlowLayout = UICollectionViewFlowLayout()
layout.sectionInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
layout.itemSize = CGSize(width: 120, height: 90)
collectionView = UICollectionView(frame: self.view.frame, collectionViewLayout: layout)
collectionView!.dataSource = self
collectionView!.delegate = self
collectionView!.registerClass(UICollectionViewCell.self, forCellWithReuseIdentifier: reuseIdentifier)
collectionView!.backgroundColor = UIColor.blackColor()
self.view.addSubview(collectionView!)
//Table View
tableView = UITableView(frame: self.view.frame, style: .Plain)
self.view.addSubview(tableView)
tableView.delegate = self
tableView.dataSource = self
tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: cellIdentifier)
//Image View
var frame = self.view.frame
someImage = UIImageView(frame: CGRect(x: 0, y: 0, width: frame.size.width, height: frame.size.height))
//Transitions
Pushes
let vc = SettingsViewController()
self.navigationController?.pushViewController(vc, animated: true)
Segues (have to use IB to set up segue identifiers)
//Constraints with Masonry Snap
let padding = UIEdgeInsetsMake(10, 10, 10, -50)
let superview = self.view
someView.snp_makeConstraints { make in
make.top.equalTo(superview.snp_top).with.offset(padding.top)
make.left.equalTo(superview.snp_left).with.offset(padding.left)
}
/** GUI Events **
//Gray Activity Indicator View
var activityIndicatorView = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
view.addSubview(activityIndicatorView)
activityIndicatorView.center = view.center
activityIndicatorView.startAnimating()
activityIndicatorView.removeFromSuperview()
//Touches
press = UILongPressGestureRecognizer(target: self, action: "doSomething:")
press.minimumPressDuration = 0.5
press.numberOfTapsRequired = 0
press.numberOfTouchesRequired = 1
press.delegate = self
self.view.addGestureRecognizer(press)
//Notifications
NSNotificationCenter.defaultCenter().addObserver(
self,
selector: "doSomething:",
name: someKey,
object: nil)
func doSomething(notification: NSNotification) {
}
//App Delegate setup
let rootViewController = SomeViewController()
let frame = UIScreen.mainScreen().bounds
self.window = UIWindow(frame: frame)
let navController = UINavigationController(rootViewController: rootViewController)
self.window?.rootViewController = navController
self.window?.backgroundColor = UIColor.whiteColor()
self.window?.makeKeyAndVisible()
//Delegates
protocol NewDelegate {
func doSomething()
}
In class:
var delegate: NewDelegate?
self.delegate?.doSomething()
In target class:
Inherit from NewDelegate
newObject.delegate = self
Implement the protocol methods
// ****** Loads Xib/nib from project as uiviewcontroller ****/
extension UIViewController {
static func loadFromNib() -> Self {
func instantiateFromNib<T: UIViewController>() -> T {
return T.init(nibName: String(describing: T.self), bundle: nil)
}
return instantiateFromNib()
}
}
/*****Loads ViewController by all ways : ***/
extension UIViewController {
static func instantiate<TController: UIViewController>(_ storyboardName: String) -> TController {
return instantiateFromStoryboardHelper(storyboardName)
}
static func instantiate<TController: UIViewController>(_ storyboardName: String, identifier: String) -> TController {
return instantiateFromStoryboardHelper(storyboardName, identifier: identifier)
}
fileprivate static func instantiateFromStoryboardHelper<T: UIViewController>(_ name: String, identifier: String? = nil) -> T {
let storyboard = UIStoryboard(name: name, bundle: Bundle(for: self))
return storyboard.instantiateViewController(withIdentifier: identifier ?? String(describing: self)) as! T
}
static func instantiate<TController: UIViewController>(xibName: String? = nil) -> TController {
return TController(nibName: xibName ?? String(describing: self), bundle: Bundle(for: self))
}
}
/ ***** End Load a ViewController ***** /
extension String {
func replaceOccuranceOfSpaceInURLString() -> String {
return self.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed) ?? self
}
var boolValue: Bool {
return (self as NSString).boolValue
}
var isBackSpace:Bool {
let char = self.cString(using: String.Encoding.utf8)!
let isBackSpace = strcmp(char, "\\b")
return isBackSpace == -92
}
}
// Storing a property in a class using Static property.
extension UIView {
struct Holder {
static var _padding:[UIView:UIEdgeInsets] = [:]
}
var padding : UIEdgeInsets {
get{ return UIView.Holder._padding[self] ?? .zero}
set { UIView.Holder._padding[self] = newValue }
}
}
//Button Extension that makes button with icon at top
// -----[image]-----------
// ----title of button----
// -----------------------
extension UIButton {
func centerVertically(padding: CGFloat = 6.0) {
guard
let imageViewSize = self.imageView?.frame.size,
let titleLabelSize = self.titleLabel?.frame.size else {
return
}
let totalHeight = self.bounds.size.height
self.imageEdgeInsets = UIEdgeInsets(
top: max(0, -(totalHeight - imageViewSize.height)),
left: 0.0,
bottom: 0.0,
right: -titleLabelSize.width
)
self.titleEdgeInsets = UIEdgeInsets(
top: 0.0,
left: -imageViewSize.width,
bottom: -(totalHeight - titleLabelSize.height),
right: 0.0
)
self.contentEdgeInsets = UIEdgeInsets(
top: 0.0,
left: 0.0,
bottom: titleLabelSize.height,
right: 0.0
)
}
}
//Extension - shortcut to add child vc
extension UIViewController {
func add(_ child: UIViewController) {
addChild(child)
view.addSubview(child.view)
child.didMove(toParent: self)
}
func remove() {
// Just to be safe, we check that this view controller
// is actually added to a parent before removing it.
guard parent != nil else {
return
}
willMove(toParent: nil)
view.removeFromSuperview()
removeFromParent()
}
}
@byJeevan
Copy link
Author

byJeevan commented Jul 22, 2020

/*Dependency injection of View Model - when a view controller instantiating from XIB nib */

//view controller

class PaymentAckScreenVC: UIViewController
{
    //MARK:- Private vars
    private var viewModel:PaymentAckVM?

    //MARK:- Inject view model
    init(viewModel:PaymentAckVM) {
        super.init(nibName: "PaymentAckScreenVC", bundle: .main)

         ///if this view controller is loaded from a storyboard
        /// super.init(nibName: nil, bundle: nil)

        self.viewModel = viewModel
    }

    required init?(coder: NSCoder) {
        super.init(coder: coder)
    }
    
    //MARK:- View Life Cycle
    override func viewDidLoad() {
        super.viewDidLoad()
        self.initialSetting()
        self.pageAppearance()
    }
..... more code 

}

// Instantiating

        let viewModel = PaymentAckVM(withPGResponse: pgResponse)
        let ackViewController = PaymentAckScreenVC(viewModel: viewModel)
        ackViewController.title = title ?? "transaction_details".localized()
        self?.navigationController?.pushViewController(ackViewController, animated: true)

@byJeevan
Copy link
Author

SingleTon

@objc
class RestoreScreenManager : NSObject {

private override init() { }

static let shared: RestoreScreenManager = {
    let instance = RestoreScreenManager()
    // Setup code if any
    return instance
}()

}

@byJeevan
Copy link
Author

byJeevan commented Aug 18, 2020

extension UIView {
    /** Loads instance from nib with the same name. */
    func loadNib() -> UIView {
        let bundle = Bundle(for: type(of: self))
        let nibName = type(of: self).description().components(separatedBy: ".").last!
        let nib = UINib(nibName: nibName, bundle: bundle)
        return nib.instantiate(withOwner: self, options: nil).first as! UIView
    }
}


extension UIViewController {
    static func loadFromNib() -> Self {
        func instantiateFromNib<T: UIViewController>() -> T {
            return T.init(nibName: String(describing: T.self), bundle: nil)
        }

        return instantiateFromNib()
    }
}


@byJeevan
Copy link
Author

byJeevan commented Aug 26, 2020

//1.Create UIView extension

extension UIView {
    class func initFromNib<T: UIView>() -> T {
        return Bundle.main.loadNibNamed(String(describing: self), owner: nil, options: nil)?[0] as! T
    }
}

//2. Create MyCustomView

class MyCustomView: UIView {

    @IBOutlet weak var messageLabel: UILabel!

    static func instantiate(message: String) -> MyCustomView {
        let view: MyCustomView = initFromNib()
        view.messageLabel.text = message
        return view
    }
}

ref:https://stackoverflow.com/a/52831749/3632832

@byJeevan
Copy link
Author

byJeevan commented Sep 19, 2020

Safe array:

extension Collection {
    subscript (safe index: Index) -> Element? {
        return indices.contains(index) ? self[index] : nil
    }
}

let data = [1, 3, 4]
data[safe: 1] // Optional(3)
data[safe: 10] // nil

@byJeevan
Copy link
Author

byJeevan commented Oct 16, 2020

//Fit view to parent

extension UIView {
    func pinEdges(to other: UIView) {
        leadingAnchor.constraint(equalTo: other.leadingAnchor).isActive = true
        trailingAnchor.constraint(equalTo: other.trailingAnchor).isActive = true
        topAnchor.constraint(equalTo: other.topAnchor).isActive = true
        bottomAnchor.constraint(equalTo: other.bottomAnchor).isActive = true
    }
}

@byJeevan
Copy link
Author

byJeevan commented Dec 1, 2020

func takeScreenshot(view: UIView) -> UIImageView {
        UIGraphicsBeginImageContext(view.frame.size)
        view.layer.renderInContext(UIGraphicsGetCurrentContext())
        let image = UIGraphicsGetImageFromCurrentImageContext()
        UIGraphicsEndImageContext()
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
        
        return UIImageView(image: image)
    }

@byJeevan
Copy link
Author

byJeevan commented Mar 3, 2021


public extension UITableView {
    
    /**
     Register nibs faster by passing the type - if for some reason the `identifier` is different then it can be passed
     - Parameter type: UITableViewCell.Type
     - Parameter identifier: String?
     */
    func registerCell(type: UITableViewCell.Type, identifier: String? = nil) {
        let cellId = String(describing: type)
        register(UINib(nibName: cellId, bundle: nil), forCellReuseIdentifier: identifier ?? cellId)
    }
    
    func registerHeaderFooterView<T: UITableViewHeaderFooterView>(_ : T.Type) {
        register(UINib(nibName: T.viewIdentifier, bundle: nil), forHeaderFooterViewReuseIdentifier: T.viewIdentifier)
    }
    /**
     DequeueCell by passing the type of UITableViewCell
     - Parameter type: UITableViewCell.Type
     */
    func dequeueCell<T: UITableViewCell>(withType type: UITableViewCell.Type) -> T? {
        return dequeueReusableCell(withIdentifier: type.cellIdentifier) as? T
    }
    
    /**
     DequeueCell by passing the type of UITableViewCell and IndexPath
     - Parameter type: UITableViewCell.Type
     - Parameter indexPath: IndexPath
     */
    func dequeueCell<T: UITableViewCell>(withType type: UITableViewCell.Type, for indexPath: IndexPath) -> T? {
        return dequeueReusableCell(withIdentifier: type.cellIdentifier, for: indexPath) as? T
    }
    
    
    func dequeueReusableCell<T: UITableViewCell>(forIndexPath indexPath: IndexPath) -> T {
        guard let cell = dequeueReusableCell(withIdentifier: T.cellIdentifier, for: indexPath) as? T else {
            fatalError("Could not dequeue cell with identifier: \(T.cellIdentifier)")
        }
        
        return cell
    }
    
    func dequeueHeaderFooterView<T: UITableViewHeaderFooterView>() -> T {
        guard let view = dequeueReusableHeaderFooterView(withIdentifier: T.viewIdentifier) as? T else {
            fatalError("Could not dequeue cell with identifier: \(T.viewIdentifier)")
        }
        
        return view
    }
}

public extension UITableViewCell {

    static var cellIdentifier: String {
        return String(describing: self)
    }

}

extension UITableViewHeaderFooterView {
    static var viewIdentifier: String {
        return String(describing: self)
    }
}

@byJeevan
Copy link
Author

byJeevan commented Mar 3, 2021

Shadow for CollectionView Cell > content view .

`extension UICollectionViewCell {
func shadowDecorate() {
let radius: CGFloat = 10

    self.contentView.layer.cornerRadius = radius
    // Always mask the inside view
    self.contentView.layer.masksToBounds = true

    self.layer.shadowColor = UIColor.black.cgColor
    self.layer.shadowOffset = CGSize(width: 0, height: 1.0)
    self.layer.shadowRadius = 3.0
    self.layer.shadowOpacity = 0.5
    // Never mask the shadow as it falls outside the view
    self.layer.masksToBounds = false

    // Matching the contentView radius here will keep the shadow
    // in sync with the contentView's rounded shape
    self.layer.cornerRadius = radius
}

}
`

@byJeevan
Copy link
Author

byJeevan commented Mar 29, 2021

Codable

   let decoder = JSONDecoder()
    do {
        let model = try decoder.decode(TestModel.self, from: data)
        //do something with model ...
    } catch {
        print(error)
    }

//Updated :


    static func parse<T: Codable>(_ model: T.Type,
                                   from apiResponse: [AnyHashable: Any]) -> T? {
         
         if let jsonData = try? JSONSerialization.data(withJSONObject: apiResponse, options: .prettyPrinted) {
             let decoder = JSONDecoder()
             do {
                return try decoder.decode(model.self, from: jsonData)
             }
             catch let error {
                 print("Parsing Error : \(error)")
             }
         }
         return nil
     }

@byJeevan
Copy link
Author

class SelfSizedTableView: UITableView {
  var maxHeight: CGFloat = UIScreen.main.bounds.size.height
  
  override func reloadData() {
    super.reloadData()
    self.invalidateIntrinsicContentSize()
    self.layoutIfNeeded()
  }
  
  override var intrinsicContentSize: CGSize {
    let height = min(contentSize.height, maxHeight)
    return CGSize(width: contentSize.width, height: height)
  }
}

Note: Another aspect to highlight is since our auto layout constraints don’t define the height of the table view, you might see missing constraintswarnings: This warning is easy to fix. Go to the size inspector and set Intrinsic size -> Placeholder:

Ref: https://dushyant37.medium.com/swift-4-recipe-self-sizing-table-view-2635ac3df8ab

@byJeevan
Copy link
Author

Read JSON from Local

static func readJSONFromFile(fileName: String) -> Any?
  {
      var json: Any?
      if let path = Bundle.main.path(forResource: fileName, ofType: "json") {
          do {
              let fileUrl = URL(fileURLWithPath: path)
              // Getting data from JSON file using the file URL
              let data = try Data(contentsOf: fileUrl, options: .mappedIfSafe)
              json = try? JSONSerialization.jsonObject(with: data)
          } catch {
              // Handle error here
          }
      }
      return json
  }

@byJeevan
Copy link
Author

//Print all the installed font

 for family in UIFont.familyNames {
        print("family:", family)
        for font in UIFont.fontNames(forFamilyName: family) {
            print("font:", font)
        }
    }

@jeevan2628
Copy link

jeevan2628 commented May 20, 2021

Line Spacing of UILabel

      extension UILabel {
          var customLineSpace: CGFloat {
              get {
                  return 0
              }
              set {
                  let textAlignment = self.textAlignment
                  let paragraphStyle = NSMutableParagraphStyle()
                  paragraphStyle.lineSpacing = newValue
                  let attributedString = NSAttributedString(string: self.text ?? "", attributes: [NSAttributedString.Key.paragraphStyle: paragraphStyle])
                  self.attributedText = attributedString
                  self.textAlignment = textAlignment
              }
          }
      }

Usage : myLabel.customLineSpace = 14.0

@jeevan2628
Copy link

Detect Scrollview reached to End or Top

extension UIScrollView {

	var scrolledToTop: Bool {
		let topEdge = 0 - contentInset.top
		return contentOffset.y <= topEdge
	}

	var scrolledToBottom: Bool {
		let bottomEdge = contentSize.height + contentInset.bottom - bounds.height
		return contentOffset.y >= bottomEdge
	}

}

@jeevan2628
Copy link

jeevan2628 commented May 27, 2021

User Default extension simplified:

extension UserDefaults {
  var onboardingCompleted: Bool {
    get { return bool(forKey: #function) }
    set { set(newValue, forKey: #function) }
  }

  var currentLanguageCode: String {
    get { return string(forKey: #function) ?? "en" }
    set { set(newValue, forKey: #function) }
  }

   var highScore: Int {
    get { return integer(forKey: #function) }
    set { set(newValue, forKey: #function) }
  }
}

@byJeevan
Copy link
Author

byJeevan commented Dec 2, 2021

    let networkInfo = CTTelephonyNetworkInfo()
    let carrierTypeString = networkInfo.serviceCurrentRadioAccessTechnology!.values.first!
    switch carrierTypeString {
    case CTRadioAccessTechnologyGPRS,CTRadioAccessTechnologyEdge,CTRadioAccessTechnologyCDMA1x: return "2G"
    case CTRadioAccessTechnologyWCDMA,CTRadioAccessTechnologyHSDPA,CTRadioAccessTechnologyHSUPA,CTRadioAccessTechnologyCDMAEVDORev0,CTRadioAccessTechnologyCDMAEVDORevA,CTRadioAccessTechnologyCDMAEVDORevB,CTRadioAccessTechnologyeHRPD: return "3G"
    case CTRadioAccessTechnologyLTE: return "4G"
    default: return ""

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment