记录一些iOS学习过程中的笔记
iOS开发-零碎笔记
创建项目目录结构
- AppDelegate.swift:生命周期及变量的定义
- ViewController.swift: MVC的C
- Assets.xcasset:放资源文件,如图片等
- info.plist:配置文件
- xxxTest: 单元测试
- Products:生成的文件
- Main.storyboard: 视图
快捷添加注释
option + command + /
复制代码
关闭软键盘
关闭代码
textField.resignFirstResponder()
复制代码
关闭方式1: 在Controller中重写touchesEnded()方法,然后在这里面关闭软件盘,意思是点击空白处关闭
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
name.resignFirstResponder()
}
复制代码
关闭方式2: 点击下一步时,关闭软键盘; Controller实现UITextFieldDelegate协议; 实现UITextFieldDelegate协议中的textFieldShouldReturn方法;
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
textField.resignFirstResponder()
return true
}
复制代码
UIDatePicker选择时间后计算年龄
func calAge(by datePicker: UIDatePicker) -> Int? {
let gregorian = NSCalendar(calendarIdentifier: .gregorian)
let now = Date()
let components = gregorian?.components(NSCalendar.Unit.year, from: datePicker.date, to: now, options: NSCalendar.Options.init(rawValue: 0))
return components?.year
}
复制代码
页面跳转,传递数据
有两个Controller:ViewController和GalleryViewController。从ViewController跳转到GalleryViewController。 ViewController重写方法:prepare,该方法在页面跳转时会被调用,我们需要在里面判断是跳转到哪个页面。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// 需要给Segue取名
if segue.identifier == "GoToGallery" {
let index = beautyPicker.selectedRow(inComponent: 0)
var imageName: String?
switch index {
case 0:
imageName = "fangbingbing"
case 1:
imageName = "libingbing"
case 2:
imageName = "wangfei"
case 3:
imageName = "yangmi"
case 4:
imageName = "zhouxu"
default:
imageName = nil
}
// 得到下一个页面的Controller
let vc = segue.destination as! GalleryViewController
vc.imageName = imageName
}
}
复制代码
通过图片文件名设置图片
beautyImage.image = UIImage(named: imageName)
复制代码
unwind segue关闭页面
关闭页面后,Controller可以获得上个页面传回来的值 该方法写在前一个页面
@IBAction func closedPrePage(segue: UIStoryboardSegue) {
print("closed")
}
复制代码
TableView下移一个状态栏的高度解决
- 方法一
if #available(iOS 11.0, *) {
tableView.contentInsetAdjustmentBehavior = .never
}
复制代码
- 方法二,内容上部分区域向上偏移一个状态栏的高度
collectionView?.contentInset.top = -UIApplication.shared.statusBarFrame.height
复制代码
TableView 添加刷新
let refreshControl = UIRefreshControl()
// 初始化刷新
refreshControl.backgroundColor = UIColor.blue //设置刷新的背景颜色
refreshControl.attributedTitle = NSAttributedString(string: "刷新一下:\(Data())", attributes: [NSAttributedStringKey.foregroundColor: UIColor.white]) // 设置字体颜色
refreshControl.tintColor = UIColor.green // 加载菊花颜色
refreshControl.tintAdjustmentMode = .dimmed // 色彩调整模式
refreshControl.addTarget(self, action: #selector(addcount), for: .valueChanged) //添加方法目标
// 添加该刷新
tableView.refreshControl = refreshControl
复制代码
刷新方法
@objc func addcount() {
dataArrary.append(contentsOf: dataArrary)
tableView.reloadData()
refreshControl.endRefreshing()
}
复制代码
向项目添加字体
developer.apple.com/documentati…
由于iOS的用的字体名称并不是文件名称,而是字体本身名称。 下面代码搜索所有字体,然后我们在控制台,找到多出来的名称。
for family: String in UIFont.familyNames
{
print("\(family)")
for names: String in UIFont.fontNames(forFamilyName: family)
{
print("== \(names)")
}
}
复制代码
设置tabbar 字体和字体大小
override func viewDidLoad() {
super.viewDidLoad()
let appearance = UITabBarItem.appearance()
appearance.setTitleTextAttributes([NSAttributedStringKey.font: UIFont(name: "Ubuntu-Light", size: 9)!], for: .normal)
}
复制代码
UIButton 相关
UIButton.isEnabled = false
后图片按钮的背景图片被改变
UIButton.adjustsImageWhenDisabled = false
复制代码
扩展添加圆角、边框、边框颜色
@IBDesignable extension UIButton {
@IBInspectable var borderWidth: CGFloat {
set {
layer.borderWidth = newValue
}
get {
return layer.borderWidth
}
}
@IBInspectable var cornerRadius: CGFloat {
set {
layer.cornerRadius = newValue
}
get {
return layer.cornerRadius
}
}
@IBInspectable var borderColor: UIColor? {
set {
guard let uiColor = newValue else { return }
layer.borderColor = uiColor.cgColor
}
get {
guard let color = layer.borderColor else { return nil }
return UIColor(cgColor: color)
}
}
}
复制代码
扩展图片在上,文字在下
extension UIButton {
func alignVertical(spacing: CGFloat = 6.0, imageBottom: CGFloat = 0.0) {
guard let imageSize = self.imageView?.image?.size,
let text = self.titleLabel?.text,
let font = self.titleLabel?.font
else { return }
self.titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: -(imageSize.height + spacing), right: 0.0)
let labelString = NSString(string: text)
let titleSize = labelString.size(withAttributes: [NSAttributedStringKey.font: font])
self.imageEdgeInsets = UIEdgeInsets(top: -(titleSize.height + spacing), left: 0.0, bottom: imageBottom, right: -titleSize.width)
let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0;
self.contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0)
}
}
复制代码
UITableView或UICollectionView被TabBar遮盖
UITableView调用reloadData
导致移动到列表顶部失效
UIView.animate(withDuration: 0, animations: {
self.tableView.contentOffset = CGPoint.zero
}, completion: { _ in
self.tableView.reloadData()
})
复制代码
NavigationBar导致CollectionViewCell或TableViewCell偏移
self.collectionView?.contentInsetAdjustmentBehavior = .automatic
复制代码
获取app版本
/// 获取版本名
let appVersion = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as? String
/// 获取版本号
let versionNumber = Bundle.main.infoDictionary!["CFBundleVersion"] as? String
复制代码
清理缓存
func clearCache() {
// 取出cache文件夹目录 缓存文件都在这个目录下
let cachePath = NSSearchPathForDirectoriesInDomains(FileManager.SearchPathDirectory.cachesDirectory, FileManager.SearchPathDomainMask.userDomainMask, true).first
// 取出文件夹下所有文件数组
let fileArr = FileManager.default.subpaths(atPath: cachePath!)
// 遍历删除
for file in fileArr! {
let path = cachePath?.appendingFormat("/\(file)")
if FileManager.default.fileExists(atPath: path!) {
do {
try FileManager.default.removeItem(atPath: path!)
} catch {
}
}
}
}
复制代码
打开网页本软件的appstore
// App Store URL.
let appStoreLink = "https://itunes.apple.com/cn/app/id1144351773?mt=8"
/* First create a URL, then check whether there is an installed app that can
open it on the device. */
if let url = URL(string: appStoreLink), UIApplication.shared.canOpenURL(url) {
// Attempt to open the URL.
UIApplication.shared.open(url, options: [:], completionHandler: {(success: Bool) in
if success {
print("Launching \(url) was successful")
}})
}
复制代码
设置圆形展示图像
- 设置
UIImageView
宽度和高度,假如设置为60*60 - 设置运行时属性,设置圆弧为30(正方形边长度一半)
- 勾选
Clip to Bounds
,
UIScrollView填充到顶部(去掉状态栏到空白间距)
-
Content Insets
选择Never
- 去掉选中的
Safe Area Relative Margins
UIImage 高斯模糊扩展
extension UIImage {
func blurred(radius: CGFloat) -> UIImage {
let ciContext = CIContext(options: nil)
guard let cgImage = cgImage else { return self }
let inputImage = CIImage(cgImage: cgImage)
guard let ciFilter = CIFilter(name: "CIGaussianBlur") else { return self }
ciFilter.setValue(inputImage, forKey: kCIInputImageKey)
ciFilter.setValue(radius, forKey: "inputRadius")
guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self }
return UIImage(cgImage: cgImage2)
}
}
复制代码
两个UIImage 合并扩展
extension UIImage {
func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width
let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
draw(in: CGRect(origin: CGPoint.zero, size: size))
image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
复制代码
SDWebImageView 下载图片
- 方式一
img.sd_setImage(with: URL(string: "http://url"),
placeholderImage: #imageLiteral(resourceName: "default_square")) { image, error, cacheType, url in
}
复制代码
- 方式二
SDWebImageDownloader
.shared()
.downloadImage(with: URL(string: "http://url"),
options: SDWebImageDownloaderOptions.init(rawValue: 0),
progress: nil,
completed: { image, data, error, finished in
if finished {
}
})
复制代码
AVPlayerViewController 视频播放
import AVKit
func playVideoByUrl(string: String) {
let videoURL = URL(string: string)
let player = AVPlayer(url: videoURL!)
let playerViewController = AVPlayerViewController()
playerViewController.player = player
self.present(playerViewController, animated: true) {
playerViewController.player!.play()
}
}
复制代码
为UIImageView添加的点击手势无效
- 勾选上
User Interaction Enabled
- 代码中设置
uiimageview.userInteractionEnabled = true
PHAsset获取文件路径
extension PHAsset {
func getURL(completionHandler : @escaping ((_ responseURL : URL?) -> Void)){
if self.mediaType == .image {
let options: PHContentEditingInputRequestOptions = PHContentEditingInputRequestOptions()
options.canHandleAdjustmentData = {(adjustmeta: PHAdjustmentData) -> Bool in
return true
}
self.requestContentEditingInput(with: options, completionHandler: {(contentEditingInput: PHContentEditingInput?, info: [AnyHashable : Any]) -> Void in
completionHandler(contentEditingInput!.fullSizeImageURL as URL?)
})
} else if self.mediaType == .video {
let options: PHVideoRequestOptions = PHVideoRequestOptions()
options.version = .original
PHImageManager.default().requestAVAsset(forVideo: self, options: options, resultHandler: {(asset: AVAsset?, audioMix: AVAudioMix?, info: [AnyHashable : Any]?) -> Void in
if let urlAsset = asset as? AVURLAsset {
let localVideoUrl: URL = urlAsset.url as URL
completionHandler(localVideoUrl)
} else {
completionHandler(nil)
}
})
}
}
}
复制代码
UIView 相关
通过UIView获取父UIViewController
extension UIView {
var parentViewController: UIViewController? {
var parentResponder: UIResponder? = self
while parentResponder != nil {
parentResponder = parentResponder!.next
if let viewController = parentResponder as? UIViewController {
return viewController
}
}
return nil
}
}
复制代码
String 相关
html的字符串,将代码转成对应效果
extension String {
var htmlToAttributedString: NSAttributedString? {
guard let data = data(using: .utf8) else { return NSAttributedString() }
do {
return try NSAttributedString(data: data, options: [.documentType: NSAttributedString.DocumentType.html, .characterEncoding:String.Encoding.utf8.rawValue], documentAttributes: nil)
} catch {
return NSAttributedString()
}
}
var htmlToString: String {
return htmlToAttributedString?.string ?? ""
}
}
复制代码
正则表达式匹配
/// 正则表达式匹配
extension String {
func matchingStrings(regex: String) -> [String] {
do {
let regex = try NSRegularExpression(pattern: regex)
let results = regex.matches(in: self,
range: NSRange(self.startIndex..., in: self))
return results.map {
String(self[Range($0.range, in: self)!])
}
} catch let error {
print("invalid regex: \(error.localizedDescription)")
return []
}
}
}
复制代码
Data拼接数据
extension Data {
mutating func append(_ string: String, using encoding: String.Encoding = .utf8) {
if let data = string.data(using: encoding) {
append(data)
}
}
}
复制代码
打乱数组顺序
extension Array{
mutating func randamArray() {
var list = self
for index in 0..<list.count {
let newIndex = Int(arc4random_uniform(UInt32(list.count-index))) + index
if index != newIndex {
list.swapAt(index, newIndex)
}
}
self = list
}
}
复制代码
UIImage相关
高斯模糊图片
extension UIImage {
func blurred(radius: CGFloat) -> UIImage {
let ciContext = CIContext(options: nil)
guard let cgImage = cgImage else { return self }
let inputImage = CIImage(cgImage: cgImage)
guard let ciFilter = CIFilter(name: "CIGaussianBlur") else { return self }
ciFilter.setValue(inputImage, forKey: kCIInputImageKey)
ciFilter.setValue(radius, forKey: "inputRadius")
guard let resultImage = ciFilter.value(forKey: kCIOutputImageKey) as? CIImage else { return self }
guard let cgImage2 = ciContext.createCGImage(resultImage, from: inputImage.extent) else { return self }
return UIImage(cgImage: cgImage2)
}
}
复制代码
两张图片叠加成一张图片
extension UIImage {
func overlayWith(image: UIImage, posX: CGFloat, posY: CGFloat) -> UIImage {
let newWidth = size.width < posX + image.size.width ? posX + image.size.width : size.width
let newHeight = size.height < posY + image.size.height ? posY + image.size.height : size.height
let newSize = CGSize(width: newWidth, height: newHeight)
UIGraphicsBeginImageContextWithOptions(newSize, false, 0.0)
draw(in: CGRect(origin: CGPoint.zero, size: size))
image.draw(in: CGRect(origin: CGPoint(x: posX, y: posY), size: image.size))
let newImage = UIGraphicsGetImageFromCurrentImageContext()!
UIGraphicsEndImageContext()
return newImage
}
}
复制代码
缩放图片
extension UIImage {
func scaled(withSize size: CGSize) -> UIImage {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
defer { UIGraphicsEndImageContext() }
draw(in: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height))
return UIGraphicsGetImageFromCurrentImageContext()!
}
}
复制代码
Json相关
Json编码
extension JSONEncoder {
/// 将实体类转换成Json数据
func toJson<T: Encodable>(_ entity: T) -> String? {
guard let encodedData = try? encode(entity) else {
return nil
}
return String(data: encodedData, encoding: .utf8)
}
}
复制代码
Json解码
extension JSONDecoder {
func from<T: Decodable>(_ type: T.Type, json: String) -> T? {
do {
return try decode(type, from: json.data(using: .utf8)!)
}
catch {
return nil
}
}
}
复制代码
请求字段编码为字符串,形式如:key=value&key=value&key=value
extension Dictionary {
func percentEscaped() -> String {
return map { (key, value) in
let escapedKey = "\(key)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
let escapedValue = "\(value)".addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed) ?? ""
return escapedKey + "=" + escapedValue
}
.joined(separator: "&")
}
}
extension CharacterSet {
static let urlQueryValueAllowed: CharacterSet = {
let generalDelimitersToEncode = ":#[]@" // does not include "?" or "/" due to RFC 3986 - Section 3.4
let subDelimitersToEncode = "!$&'()*+,;="
var allowed = CharacterSet.urlQueryAllowed
allowed.remove(charactersIn: "\(generalDelimitersToEncode)\(subDelimitersToEncode)")
return allowed
}()
}
复制代码
UIViewController 相关
添加子UIViewController
extension UIViewController {
/// 添加子ViewController
func addSubController(child: UIViewController, to: UIView? = nil) {
addChildViewController(child)
if let to = to {
child.view.frame = to.frame
to.addSubview(child.view)
}
else {
child.view.frame = view.frame
view.addSubview(child.view)
}
child.didMove(toParentViewController: self)
}
}
复制代码
移除子UIViewController
extension UIViewController {
/// 移除子ViewController
func removeSubController(child: UIViewController) {
child.willMove(toParentViewController: nil)
child.removeFromParentViewController()
child.view.removeFromSuperview()
}
}
复制代码
关闭页面
关闭当前页面
extension UIViewController {
/// 关闭当前页面
func closePage() {
self.dismiss(animated: true, completion: nil)
}
}
复制代码
关闭所有页面,除开最下级的那个页面
extension UIViewController {
func closeAllPage() {
//获取根VC
var rootVC = self.presentingViewController
while let parent = rootVC?.presentingViewController {
rootVC = parent
}
//释放所有下级视图
rootVC?.dismiss(animated: true, completion: nil)
}
}
复制代码
显示和关闭菊花等待加载
extension UIViewController {
class func displaySpinner(onView : UIView) -> UIView {
let spinnerView = UIView.init(frame: onView.bounds)
spinnerView.backgroundColor = UIColor.init(red: 0.5, green: 0.5, blue: 0.5, alpha: 0.5)
let ai = UIActivityIndicatorView.init(activityIndicatorStyle: .whiteLarge)
ai.startAnimating()
ai.center = spinnerView.center
DispatchQueue.main.async {
spinnerView.addSubview(ai)
onView.addSubview(spinnerView)
}
return spinnerView
}
class func removeSpinner(spinner :UIView) {
DispatchQueue.main.async {
spinner.removeFromSuperview()
}
}
}
复制代码
显示
let sp = UIViewController.displaySpinner(onView: self.view)
复制代码
关闭
UIViewController.removeSpinner(spinner: sp)
复制代码
IAP 内购
使用
- 除代码外的内购准备工序已OK
- 获取产品数据:通过
IAPHelper.shared.fetchAvailableProducts
从苹果服务器获取所有传入的产品id的产品信息,传入的参数是产品的id字符串数组 - 支付:
IAPHelper.shared.purchase(id: id)
,id是产品id
IAPHelper.shared.purchase(id: selectItem!.product_id) {alert, product, transaction in
if alert == .purchased { //购买成功
if let receiptUrl = Bundle.main.appStoreReceiptURL, let receiptData = NSData(contentsOf: receiptUrl) {
let receiptString = receiptData.base64EncodedString(options: NSData.Base64EncodingOptions(rawValue: 0))
// 对receiptString加密字符串进行验证
}
}
else if alert == .restored {
}
else if alert == .purchasing {
}
else {
}
}
复制代码
IAPHelper 代码
import StoreKit
enum IAPHelperAlertType{
case disabled
case restored
case purchased
case purchasing
case setProductIds
func message() -> String{
switch self {
case .setProductIds: return "未设置产品id,请调用 fetchAvailableProducts()"
case .disabled: return "购买已取消"
case .restored: return "您已成功恢复购买"
case .purchased: return "您已成功购买此商品"
case .purchasing: return "正在购买..."
}
}
}
class IAPHelper: NSObject {
static let shared = IAPHelper()
private override init() { }
fileprivate var productID = ""
fileprivate var productsRequest = SKProductsRequest()
fileprivate var productDict = [String:SKProduct]()
fileprivate var fetchProductCompletion: (([SKProduct])->Void)?
fileprivate var productToPurchase: SKProduct?
var purchaseProductCompletion: ((IAPHelperAlertType, SKProduct?, SKPaymentTransaction?) -> Void)?
// MARK: - 购买产品
func canMakePurchases() -> Bool { return SKPaymentQueue.canMakePayments() }
func purchase(id: String, completion: @escaping ((IAPHelperAlertType, SKProduct?, SKPaymentTransaction?)->Void)) {
self.purchaseProductCompletion = completion
self.productToPurchase = productDict[id]
guard let product = self.productToPurchase else {
print(IAPHelperAlertType.setProductIds.message())
fatalError(IAPHelperAlertType.setProductIds.message())
}
if self.canMakePurchases() {
let payment = SKPayment(product: product)
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().add(payment)
print("采购产品: \(product.productIdentifier)")
productID = product.productIdentifier
}
else {
completion(.disabled, nil, nil)
}
}
// MARK: - 恢复购买
func restorePurchase(){
SKPaymentQueue.default().add(self)
SKPaymentQueue.default().restoreCompletedTransactions()
}
// MARK: - 获取可用的iap产品
func fetchAvailableProducts(by ids: [String], completion: @escaping (([SKProduct])->Void)){
self.fetchProductCompletion = completion
// 把您的IAP产品id放到这里面
guard !ids.isEmpty else {
print("没有设置产品id")
fatalError(IAPHelperAlertType.setProductIds.message())
}
productsRequest = SKProductsRequest(productIdentifiers: Set(ids))
productsRequest.delegate = self
productsRequest.start()
}
}
extension IAPHelper: SKProductsRequestDelegate, SKPaymentTransactionObserver{
// MARK: - 请求IAP产品
func productsRequest (_ request:SKProductsRequest, didReceive response:SKProductsResponse) {
if (response.products.count > 0) {
for product in response.products {
print("product.productIdentifier = \(product.productIdentifier)")
self.productDict[product.productIdentifier] = product
}
self.fetchProductCompletion?(response.products)
}
}
func paymentQueueRestoreCompletedTransactionsFinished(_ queue: SKPaymentQueue) {
self.purchaseProductCompletion?(.restored, nil, nil)
}
// MARK:- IAP付款队列
func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) {
print("调用了几次啊!!!")
for transaction:AnyObject in transactions {
if let trans = transaction as? SKPaymentTransaction {
switch trans.transactionState {
case .purchased:
print("产品已购买")
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
self.purchaseProductCompletion?(.purchased, self.productToPurchase, trans)
break
case .failed:
print("产品购买失败\(trans.error.debugDescription)")
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
self.purchaseProductCompletion?(.disabled, self.productToPurchase, trans)
break
case .purchasing:
print("正在购买...")
self.purchaseProductCompletion?(.purchasing, self.productToPurchase, trans)
break
case .restored:
print("产品已恢复购买")
SKPaymentQueue.default().finishTransaction(transaction as! SKPaymentTransaction)
self.purchaseProductCompletion?(.restored, self.productToPurchase, trans)
break
default: break
}
}
}
}
}
复制代码