从100 Days of Swift中学习,实践
目前正在学习swift , 刚刚接触了解了一部分语法后就因为自己在OC上使用reactiveOBJC还算熟练,想直接学会rxswift和reactiveswift ,中间因为xcode有时候索引失效和一些其他原因,想过放弃学习,无意中看到 关于iOS学习进阶的必读一些博客总结 这个文章时看到了 100 Days of Swift, 感觉从一次次项目中,更加能够坚实我的基础, 所以决定从基础开始,跟着一步步往前走
Day 4
可以先来说一下我的ViewController分层
代码:
//MARK:-视图加载
//MARK:-自定义方法
//MARK:-事件
//MARK:-代理方法
//MARK:-数据源
//MARK:-生命周期
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:-懒加载
当然在extension 里面实现delegate
PROJECT 7 - PASSING DATA TO ANOTHER VIEW
传递数据到另一个view**
代码:
//FirstViewController
let secondVC:SecondViewController = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
secondVC.restoreText = self.message
//SecondViewController
var restoreText:String?
PROJECT 8 - SWIPE TO DISMISS KEYBOARD
实现一个向下拖拽的方法,使键盘消失
代码:
@IBOutlet weak var MessageTextView: UITextView!
fileprivate func initEvents(){
let swipeDownGesture = UISwipeGestureRecognizer(target: self, action:#selector(downGesture(_:)))
swipeDownGesture.direction = UISwipeGestureRecognizerDirection.down //不设置是右
MessageTextView.addGestureRecognizer(swipeDownGesture)
}
@objc fileprivate func downGesture(_ gesture:UISwipeGestureRecognizer){
MessageTextView.resignFirstResponder()
}
PROJECT 9 - ADD PHOTO FROM CAMERA ROLL
- Access the Camera Roll from within the App
- Create Image Picker Controller
- Handle a selected image in the Camera Roll
- Control how the image is displayed to prevent stretching
主要难点在于添加图片到textview中
思路:
1.首先要能够进入相册(我这里直接使用了相册, 并没有使用camera)
2.选中图片后获取到图片
3.富文本内容, 用NSAttributeString
承接
4.在接收到图片以后需要进行哪里处理?
先将已有内容保存下来,然后用NSAttachment
接收到Image
,然后通过其内的方法转为NSAttributeString
需要注意的事项
-
NSAttachment.bounds
可以用来调整图片大小和图片位置 - 在添加完图片以后需要调整光标位置,并且将view移动到光标处,并且需要设置内容的字体大小(之前设置的是textFont, NSAttributedText的字体还未设置)
未解决的问题
- 在textview中 直接在
NSAttributerString
中追加"\n" 或者"\r\n" 无法换行,如果设置图片的模式是适配textview的宽度,那么就会导致添加图片后,光标在图片右边,而换行后的位置。 目前我还没有好的解决方案, 只能先做成在添加完图片以后,textview失去第一响应,来让用户自己选择光标位置。如果有什么好的解决方案, 请联系我!!
(学习过程用, 遇到不太会的知识, 尽量去找文档, 翻Dash,也许会比直接baidu现成的代码更好)
代码
import UIKit
enum ImageAttachmentMode {
case Default //默认(不改变大小)
case FitTextLine //使尺寸适应行高
case FitTextView //使尺寸适应textView
}
class ViewController: UIViewController {
@IBOutlet weak var MessageTextView: UITextView!
var message:String?
//MARK:-视图加载
fileprivate func setupUI() {
// let rightItem = UIBarButtonItem.init(title: "Done", style: UIBarButtonItemStyle.done, target: self, action: #selector(rightBtnClick(_:)))
let rightItem = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.camera, target: self, action: #selector(rightBtnClick(_:)))
// rightItem.setBackgroundImage(UIImage(), for: UIControlState.normal, barMetrics: UIBarMetrics.default)
// rightItem.setBackgroundImage(UIImage(), for: UIControlState.highlighted, barMetrics: UIBarMetrics.default)
// rightItem.
self.navigationItem.rightBarButtonItem = rightItem
}
//MARK:-自定义方法
fileprivate func initEvents(){
let swipeDownGesture = UISwipeGestureRecognizer(target: self, action:#selector(downGesture(_:)))
swipeDownGesture.direction = UISwipeGestureRecognizerDirection.down //不设置是右
MessageTextView.addGestureRecognizer(swipeDownGesture)
}
fileprivate func insertPicture(_ image:UIImage,_ model:ImageAttachmentMode = .Default){
//将现存所有文字转为 NSMutableAttributedString
let mutableString = NSMutableAttributedString.init()
mutableString.append(MessageTextView.attributedText)
//获取光标的位置
let mutableSelectRange = MessageTextView.selectedRange
//创建附件,来保存图片
let imgAttachment = NSTextAttachment.init()
//用来保存附件 attachment转化的attributeString
var imageAttributeString:NSAttributedString
//主要两个属性 一个image,图片资源, 一个bounds 图片位置
imgAttachment.image = image
//根据模式选择图片放置的模式
switch model {
//适应文字大小
case .FitTextLine:
imgAttachment.bounds = CGRect.init(x: 0, y: -4, width: (MessageTextView.font?.lineHeight)!, height: (MessageTextView.font?.lineHeight)!)
//适应textview的宽度 撑满一行
case .FitTextView:
let imageWidth = MessageTextView.bounds.width-10
let imageHeight = image.size.height/image.size.width*imageWidth
imgAttachment.bounds = CGRect.init(x: 0, y: 0, width: imageWidth, height: imageHeight)
default:
break
}
imageAttributeString = NSAttributedString.init(attachment: imgAttachment)
mutableString.append(imageAttributeString)
mutableString.addAttribute(NSAttributedStringKey.font, value: UIFont.systemFont(ofSize: 17), range: NSMakeRange(0,mutableString.length))
//记住新的光标位置
let newSelectRange = NSMakeRange(mutableSelectRange.location+1,0)
//赋值到textview
MessageTextView.attributedText = mutableString
//刷新光标
MessageTextView.selectedRange = newSelectRange
//滚动到光标位置
MessageTextView.scrollRangeToVisible(newSelectRange)
}
//MARK:-事件
@objc fileprivate func rightBtnClick(_ sender:UIBarButtonItem){
// let secondVC:SecondViewController = UIStoryboard.init(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "SecondViewController") as! SecondViewController
// secondVC.restoreText = self.message
// self.navigationController?.pushViewController(secondVC, animated: true)
//访问相册页面
//从Dash文档上根据文档说明,步骤进行开发
if UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceType.photoLibrary){
UIImagePickerController.availableMediaTypes(for: UIImagePickerControllerSourceType.photoLibrary)
let vc = UIImagePickerController.init()
vc.delegate = self
self.present(vc, animated: true, completion: {
})
}
else {
}
}
@objc fileprivate func downGesture(_ gesture:UISwipeGestureRecognizer){
MessageTextView.resignFirstResponder()
}
//MARK:-代理方法
//MARK:-数据源
//MARK:-生命周期
override func viewDidLoad() {
super.viewDidLoad()
setupUI()
initEvents()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:-懒加载
}
extension ViewController: UIImagePickerControllerDelegate,UINavigationControllerDelegate{
func imagePickerControllerDidCancel(_ picker: UIImagePickerController) {
}
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {
let image = info[UIImagePickerControllerOriginalImage]
self.insertPicture(image as! UIImage,.FitTextView)
self.MessageTextView.resignFirstResponder()
picker.dismiss(animated: true, completion: nil)
}
}
extension ViewController:UITextViewDelegate{
func textViewDidEndEditing(_ textView: UITextView) {
message = textView.text
textView.resignFirstResponder()
}
}
PROJECT 10 - PULL TO REFRESH TABLE VIEW (未完成自定义refresh功能)
- Build a custom Table View Controller
- Create custom Refresh Control
- Stop refresh animation when data finishes updating
- Update the table with refreshed local data
还在学习MJRefresh源码中, 之后补上
PROJECT 11 - DELETING AND REARRANGING
- Remove data from Data Source
- Delete data from Table Row
- Animate the item deletion
- Handle rearranging Table Rows
- Enable swipe to delete Table Row
PROJECT 12 - ADD NEW ITEM
- Create a Model to interact with View Controllers
- Add data to the Model
- Update the Table View when the View loads
- Dismiss the View from the Keyboard Done key
- Segue to new Views from a Button (没有使用segue)
除了project 10 其他内容也相对简单,整合在一起记录。
- 使用ViewModel的Array 作为tableview的数据源
- 查找tableview的delegate方法, 删除,移动,根据操作修改ViewModel的Array数据
- 从SecondViewController中点击Done 添加此数据到ViewModel的Array中。并且通过闭包回调到FirstViewController,刷新tableview
代码:
FirstViewController
import UIKit
class ViewController: UITableViewController {
//MARK:-视图加载
//MARK:-自定义方法
//MARK:-事件
@objc func Edit(){
self.tableView.isEditing = !self.tableView.isEditing
}
@objc func Add(){
let vc:AddViewController = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "AddViewController") as! AddViewController
self.navigationController?.pushViewController(vc, animated: true)
weak var weakself = self
//回调方法
vc.sendHandler { (str) in
weakself?.viewmodel.Movies.append(str)
weakself?.tableView.reloadData()
}
}
//MARK:-代理方法
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// UIStoryboard mainStoryboard = [UIStoryboard storyboardWithName:@"Main" bundle:nil];
// MainViewController *mainController = [mainStoryboard instantiateViewControllerWithIdentifier:@"MainViewController"];
let vc = UIStoryboard(name: "Main", bundle: nil).instantiateViewController(withIdentifier: "TimeViewController")
self.navigationController?.pushViewController(vc, animated: true)
}
override func tableView(_ tableView: UITableView, moveRowAt sourceIndexPath: IndexPath, to destinationIndexPath: IndexPath) {
let tmp:String = viewmodel.Movies[sourceIndexPath.row]
viewmodel.Movies.remove(at: sourceIndexPath.row)
viewmodel.Movies.insert(tmp, at: destinationIndexPath.row)
}
override func tableView(_ tableView: UITableView, commit editingStyle: UITableViewCellEditingStyle, forRowAt indexPath: IndexPath) {
print("\(#function)")
if editingStyle == .delete {
viewmodel.Movies.remove(at: indexPath.row)
tableView.reloadData()
}
else if editingStyle == .insert
{
}
}
//MARK:-数据源
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewmodel.Movies.count
}
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
return 50
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = viewmodel.Movies[indexPath.row]
cell.accessoryType = UITableViewCellAccessoryType.disclosureIndicator
cell.selectionStyle = .none
return cell
}
override func tableView(_ tableView: UITableView, canEditRowAt indexPath: IndexPath) -> Bool {
return true
}
//MARK:-生命周期
override func viewDidLoad() {
super.viewDidLoad()
// self.tableView.delegate = self
// self.tableView.dataSource = self
self.tableView.tableFooterView = UIView.init(frame: CGRect.zero)
self.tableView.register(object_getClass(UITableViewCell()), forCellReuseIdentifier: "cell")
let leftItem = UIBarButtonItem.init(title: "Edit", style: UIBarButtonItemStyle.plain, target: self, action: #selector(Edit))
self.navigationItem.leftBarButtonItem = leftItem
let rightAddItem = UIBarButtonItem.init(barButtonSystemItem: UIBarButtonSystemItem.add, target: self, action: #selector(Add))
self.navigationItem.rightBarButtonItem = rightAddItem
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:-懒加载
lazy var viewmodel: ViewModel = {
let vm = ViewModel()
return vm
}()
}
SecondViewController
import UIKit
class AddViewController: UIViewController {
//闭包定义
typealias SendBlockHandler = (_ str:String)->Void
var sendHandlerClosuer:SendBlockHandler?
//必须要用方法承接闭包, 不能像OC一样定义完Block以后,可以直接使用这个属性
func sendHandler(closure:@escaping SendBlockHandler) -> Void {
sendHandlerClosuer = closure
}
override func viewDidLoad() {
super.viewDidLoad()
AddTextView.becomeFirstResponder()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
@IBOutlet weak var AddTextView: UITextView!
@IBAction func SwipDownGesture(_ sender: Any) {
AddTextView.resignFirstResponder()
}
}
extension AddViewController:UITextViewDelegate{
//控制Return
func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
if text == "\n" {
textView.resignFirstResponder()
self.navigationController?.popViewController(animated: true)
sendHandlerClosuer!(textView.text)
return false
}
return true
}
}
在实际项目中实践,在书写记录中巩固,每日一记。