=======================================================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。因此也许还会有下一篇。