=======================================================html
iOS開發已經作了快4年了,據說Swift也已經有兩年多,可是一直都只是把學習停留在表面。無心中據說了有一個叫Sam Lu在Twitter上發起了一個100天作40個Swift小程序的活動,再加上國內看到了Allen_朝輝寫的Swift學習的文章,內心暗自下了一個決定:30天寫30個Swift小程序,但願能推進本身學習Swift的計劃。這30個小程序難度不一樣,有的一個晚上就能寫完,有的要佔用週末大部分時間來細研究。大部分不會的東西Google都能找到,就算Swift版本沒有找到Objective-C版本而後用Swift重寫就好,好在他們對應關係比較明確。git
用例方面,既參考了Sam Lu的40個小項目,也參考了Allen_朝輝的項目,還有的是我本身仿寫的知名App。github
其實我並非惟一在國內發起這個30天30個Swift小程序而且將其開源的做者,可是我多是惟一一個從頭至尾用XCode 8 + Swift3環境編寫的做者。並且,爲了讓代碼更加可讀,全部代碼徹底手寫,而非用Storyboard(除了只能用Storyboard的,例如apple watch app)。實際上多人協做的項目中咱們儘量少用Storyboard,由於很容易出現衝突問題。何況從學習的角度,storyboard很難說清楚操做步驟是什麼。在這上面我其實花了很多時間,可是我認爲很值得。小程序
但願能有更多對Swift感興趣的開發者加入這項#30天30個Swift小程序 的活動裏面來。如下爲Github連接: github.com/nimomeng/30…swift
triggerButton.layer.cornerRadius = triggerButton.frame.width / 2
triggerButton.layer.masksToBounds = true
複製代碼
lazy var firstWay = "first"
複製代碼
以及數組
lazy var secondWay: String = {return "Second"}()
複製代碼
注意:第二種方式要注意定義好字段類型,以便於編譯時的類型檢查;以及不要忘記最後的小括號緩存
UIScrollView的基本使用和細節小點,例如禁止彈跳的bounces屬性,整頁切換的isPagingEnabled屬性,起始位置contentOffset屬性等bash
加載子Viewcontroller的addChildViewController方法session
"xxx class has no initializers"問題:app
You have to use implicitly unwrapped optionals so that Swift can cope with
circular dependencies (parent <-> child of the UI components in this case)
during the initialization phase.
@IBOutlet var imgBook: UIImageView!
@IBOutlet var titleBook: UILabel!
@IBOutlet var pageBook: UILabel!
複製代碼
權限問題,具體錯誤描述爲: "This app has crashed because it attempted to access privacy-sensitive data without a usage description. The app's Info.plist must contain an NSPhotoLibraryUsageDescription key with a string value explaining to the user how the app uses this data." 解決方法:iOS10以後的權限問題,在info.plist裏添加相應的權限以及描述便可。 本例中權限爲: NSCameraUsageDescription PhotoMe needs the camera to take photos. NSMicrophoneUsageDescription PhotoMe needs the microphone to record audio with Live Photos. NSPhotoLibraryUsageDescription PhotoMe will save photos in the Photo Library.
AVCaptureSession 的使用方法:
AVCaptureSession
實例,並設置其sessionPreset
值,也就是設置畫面的質量。device
實例(此時決定是用Video仍是Audio),再由實例獲取其Input Source。最後將input source add到session中。captureSesssion.startRunning()
Photo的捕獲方法
AVCapturePhotoSettings
對象,並配置相應的屬性,例如是否打開flash,是否開啓防抖模式等等func capture(_ captureOutput: AVCapturePhotoOutput, didFinishProcessingPhotoSampleBuffer photoSampleBuffer: CMSampleBuffer?, previewPhotoSampleBuffer: CMSampleBuffer?, resolvedSettings: AVCaptureResolvedPhotoSettings, bracketSettings: AVCaptureBracketedStillImageSettings?, error: Error?)
複製代碼
中執行獲取圖像的具體邏輯。本例中是先將buffer轉換爲data,再轉換爲UIImage,最終write到相冊文件夾中。
UICollectionView的使用
自定義Layout要在對應的子類裏實現以下方法
prepare()
shouldInvalidateLayout(forBoundsChange newBounds: CGRect)
targetContentOffset(forProposedContentOffset proposedContentOffset: CGPoint, withScrollingVelocity velocity: CGPoint)
layoutAttributesForElements(in rect: CGRect)
複製代碼
其中:
prepare能夠定義初始化操做
shouldInvalidateLayout,判斷是否須要更新每一個Cell的bounds。若是咱們的layout是那種每一個cell須要動態變化的layout,則設置爲true;不然爲了性能考慮,請設置爲false。默認爲flase。
targetContentOffset,若是咱們須要圖片在滾動的過程當中在特定位置能夠停下來(相似iphone上專輯圖片的選擇),請在此函數中國年給出停下來的具體規則
layoutAttributesForElements 返回全部元素此時的全部佈局。咱們會在這裏定義在滾動過程當中全部其餘元素的attribute佈局相關屬性。例如本例中,離屏幕中間越近,圖片被縮放的越大;離屏幕越小,圖片被縮放的越小。
Reference:
Visual Effect View的使用
儘可能在須要模糊化的圖層以後添加進去,會自動虛化所覆蓋的圖層
let blurEffect: UIBlurEffect = UIBlurEffect(style: .light)
let blurView: UIVisualEffectView = UIVisualEffectView(effect: blurEffect)
blurView.frame = self.view.bounds
bgView.addSubview(blurView)
複製代碼
UISegmentedControl 的使用(略)
其它:#selector()中的func若是帶有參數,請將具體參數也一塊兒寫進去,例如: #selector(action_segmentValueChanged(sender:)
這個規則和OC不太同樣,要注意。
opacity
。代碼比較簡單,這裏不贅述。navigationController(_ navigationController: UINavigationController, animationControllerFor operation: UINavigationControllerOperation, from fromVC: UIViewController, to toVC: UIViewController) -> UIViewControllerAnimatedTransitioning?
複製代碼
UIViewControllerAnimatedTransitioning
須要自定義動畫,須要實現UIViewControllerAnimatedTransitioning
代理,實現具體的兩個方法:
transitionDuration(using transitionContext: UIViewControllerContextTransitioning?) -> TimeInterval
複製代碼
animateTransition(using transitionContext: UIViewControllerContextTransitioning)
複製代碼
let fromVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.from) as! XXXController
let toVC = transitionContext.viewController(forKey: UITransitionContextViewControllerKey.to) as! YYYController
let container = transitionContext.containerView
複製代碼
let snapshotView = fromVC.selectedCell.imageView.snapshotView(afterScreenUpdates: false)
snapshotView?.frame = container.convert(fromVC.selectedCell.imageView.frame, from: fromVC.selectedCell)
....
複製代碼
transitionContext.completeTransition(true)
,說明了讓navigationController來接管控制權利(在completion的block中)transitionContext.completeTransition(!transitionContext.transitionWasCancelled)
說明動畫執行完成navigationController(_ navigationController: UINavigationController, interactionControllerFor animationController: UIViewControllerAnimatedTransitioning) -> UIViewControllerInteractiveTransitioning?
複製代碼
其中,UIViewControllerInteractiveTransitioning
是動畫過渡對象
UIScreenEdgePanGestureRecognizer
方法並設置其edges爲left實現的:let edgePanGesture = UIScreenEdgePanGestureRecognizer(target: self, action: #selector(edgePanGestrueAction(_:)))
edgePanGesture.edges = UIRectEdge.left
複製代碼
animationController(forDismissed dismissed: UIViewController)
與animationController(forPresented presented: UIViewController, presenting: UIViewController, source: UIViewController)
來進行設置。#selector(CustomTransitionDelegate.functionName)
transitionContext.completeTransition(true)
fromViewController?.endAppearanceTransition()
toViewController?.endAppearanceTransition()
複製代碼
####我學到了
window = UIWindow(frame: UIScreen.main.bounds)
let rearNavigationController = UINavigationController(rootViewController: MenuViewController())
let frontNavigationController = UINavigationController(rootViewController: FrontViewController())
let revealController = SWRevealViewController(rearViewController: rearNavigationController, frontViewController: frontNavigationController)
revealController?.delegate = self
window?.rootViewController = revealController
window?.makeKeyAndVisible()
複製代碼
self.view.addGestureRecognizer(self.revealViewController().panGestureRecognizer())
複製代碼
self.navigationController?.isNavigationBarHidden = true
複製代碼
override var prefersStatusBarHidden: Bool { return true }
複製代碼
heartView.transform = CGAffineTransform(scaleX: 1.5, y: 1.5)
複製代碼
self.rollingBallView.transform = CGAffineTransform(6.28)
複製代碼
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: EntityName)
do {
let searchResults = try getContext().fetch(fetchRequest)
dataSource = searchResults as! [TodoList]
} catch {
// todo error handler
}
複製代碼
注意,或取出來的searchResult能夠直接實例化爲TodoList(TodoList是個人Entity名字),這樣後續就能夠直接使用TodoList的content方法了。
let context = getContext()
// 定義一個entity,這個entity必定要在xcdatamodeld中作好定義
let entity = NSEntityDescription.entity(forEntityName: EntityName, in: context)
let todoList = NSManagedObject(entity: entity!, insertInto: context)
todoList.setValue(content, forKey: "content"
do {
try context.save()
}catch{}
複製代碼
對應getConent方法的代碼兩行:
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
複製代碼
如此操做後使用的時候直接經過獲取TodoList對象,而後調用其content方法便可完成。 cell.textLabel?.text = (dataSource[indexPath.row]).content
alertController.addTextField { (textField) in
textField.placeholder = "Please input the todo Item"}
複製代碼
Watch程序,須要在create project的先選擇Watch OS的Section,以後選擇以下:
watch中的UI只能夠經過Storyboard來進佈局,佈局文件在WatchKit App中的Interface.storyboard中
例子中涉及到了watch和主app的交互,這裏使用的是WCSession
方法,使用步驟以下:
let wcsession = WCSession.default()
if WCSession.isSupported() {
wcsession.delegate = self
wcsession.activate()
}
複製代碼
try wcsession.updateApplicationContext(["numberToBeGuessed": number])
func session(_ session: WCSession, didReceiveApplicationContext applicationContext: [String : Any])
複製代碼
let userDefault = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")
複製代碼
let userDefaults = UserDefaults(suiteName: "group.nimoAndHisFriend.watchDemo")
var leftTimeWhenQuit = userDefaults?.double(forKey: "lefttime")
複製代碼
爲了想讓widget裏的數據也進行同步更新,能夠在extension的代碼裏也加入一個timer來進行同步操做。這樣widge和主程序的widge便可同步
import CoreSpotlight
let tmpItems = [searchItem]
CSSearchableIndex.default().indexSearchableItems(tmpItems) { (error) in
}
複製代碼
self.window?.traitCollection.forceTouchCapability == .available
UIApplicationShortcutItem
類型的Item,而後設置application的shortcutItems屬性便可。要注意,在設置icon時,只能夠設置系統內置的集中icon,不支持自定義圖標UIViewControllerPreviewingDelegate
self.registerForPreviewing(with: self, sourceView: self.view)
func previewingContext(_ previewingContext: UIViewControllerPreviewing, commit viewControllerToCommit: UIViewController)
複製代碼
usingSpringWithDamping
來完成,其中的屬性要注意:
usingSpringWithDamping:值越小動畫越誇張,借用網上圖來講明其區別:
initialSpringVelocity:值越大則起始速度越大,再借用網上圖片來講明其區別:
options的各個動畫曲線有何區別:能夠看圖來進行區分:
cell.frame.origin.y
,也能夠經過cell.transform = CGAffineTransform(translationX: 0, y: tableHeight)
,效果是同樣的。不過若是要用到縮放或者旋轉的動畫,恐怕只能使用後者了。Int(arc4random())%(emojiArray.count - 2) + 1
的方法來實現bannerImgView.transform = CGAffineTransform(scaleX: scaleFactor, y: scaleFactor)
複製代碼
簡單起見,我用Project 13的代碼基礎上進行修改,換了個清爽的綠色:)
attributedTitle
,添加好target事件(UIControlEvents.valueChanged
事件)後,添加到tableView中,便可Domain=GEOErrorDomain Code=-8 "(null)"
之類的錯誤,將 CLGeocoder
改爲全局變量便可如何修改字體屬性,熟悉字體屬性
字體名稱能夠去storyboard中查詢,或者經過以下代碼來進行查詢:
func printAllSupportedFontNames() {
let familyNames = UIFont.familyNames
for familyName in familyNames {
print("++++++ \(familyName)")
let fontNames = UIFont.fontNames(forFamilyName: familyName)
for fontName in fontNames {
print("----- \(fontName)")
}}}
複製代碼
能堅持看到這裏的,我給大家手動雙擊666!
實話實說,文章有點標題黨,實際開發時間是40天左右,由於開發時間在下班後到睡覺前,因此有時由於要出去聚餐,有時犯懶,還有時晚上要你懂得,因此完成這三十個項目的時間比計劃的時間要長。。。
寫完這些項目,感受上一方面是提升了使用Swift語言的熟練度,另外一方面更是複習了一遍iOS開發的知識點,由於寫到後來我已經基本感受不出來跟用OC開發有什麼思路上的差別。這也回答了別人問過個人問題,「若是我如今學iOS開發,是應該學OC仍是Swift」:
我以爲從iOS SDK的熟悉角度來講,沒有本質區別,若是熟悉OC下對應語法去使用Swift寫沒有太大區別。因此與其花時間糾結不如趕忙找兩個項目上手進行練習。
下一步,我打算再從新梳理下Swift語法,對這些項目進行小規模的重構,從結構上去看看可否挖掘到Swift的特性,從另外一個角度(目前是功能角度)來學習Swift。因此也許還會有下一篇。