從100 Days of Swift中學(xué)習(xí),實(shí)踐
目前正在學(xué)習(xí)swift , 剛剛接觸了解了一部分語(yǔ)法后就因?yàn)樽约涸贠C上使用reactiveOBJC還算熟練,想直接學(xué)會(huì)rxswift和reactiveswift ,中間因?yàn)閤code有時(shí)候索引失效和一些其他原因,想過放棄學(xué)習(xí),無(wú)意中看到 關(guān)于iOS學(xué)習(xí)進(jìn)階的必讀一些博客總結(jié) 這個(gè)文章時(shí)看到了 100 Days of Swift, 感覺從一次次項(xiàng)目中,更加能夠堅(jiān)實(shí)我的基礎(chǔ), 所以決定從基礎(chǔ)開始,跟著一步步往前走
Day 4
可以先來(lái)說一下我的ViewController分層
代碼:
//MARK:-視圖加載
//MARK:-自定義方法
//MARK:-事件
//MARK:-代理方法
//MARK:-數(shù)據(jù)源
//MARK:-生命周期
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
//MARK:-懶加載
當(dāng)然在extension 里面實(shí)現(xiàn)delegate
PROJECT 7 - PASSING DATA TO ANOTHER VIEW
傳遞數(shù)據(jù)到另一個(gè)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
實(shí)現(xiàn)一個(gè)向下拖拽的方法,使鍵盤消失
代碼:
@IBOutlet weak var MessageTextView: UITextView!
fileprivate func initEvents(){
let swipeDownGesture = UISwipeGestureRecognizer(target: self, action:#selector(downGesture(_:)))
swipeDownGesture.direction = UISwipeGestureRecognizerDirection.down //不設(shè)置是右
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
主要難點(diǎn)在于添加圖片到textview中
思路:
1.首先要能夠進(jìn)入相冊(cè)(我這里直接使用了相冊(cè), 并沒有使用camera)
2.選中圖片后獲取到圖片
3.富文本內(nèi)容, 用NSAttributeString
承接
4.在接收到圖片以后需要進(jìn)行哪里處理?
先將已有內(nèi)容保存下來(lái),然后用NSAttachment
接收到Image
,然后通過其內(nèi)的方法轉(zhuǎn)為NSAttributeString
需要注意的事項(xiàng)
-
NSAttachment.bounds
可以用來(lái)調(diào)整圖片大小和圖片位置 - 在添加完圖片以后需要調(diào)整光標(biāo)位置,并且將view移動(dòng)到光標(biāo)處,并且需要設(shè)置內(nèi)容的字體大小(之前設(shè)置的是textFont, NSAttributedText的字體還未設(shè)置)
未解決的問題
- 在textview中 直接在
NSAttributerString
中追加"\n" 或者"\r\n" 無(wú)法換行,如果設(shè)置圖片的模式是適配textview的寬度,那么就會(huì)導(dǎo)致添加圖片后,光標(biāo)在圖片右邊,而換行后的位置。 目前我還沒有好的解決方案, 只能先做成在添加完圖片以后,textview失去第一響應(yīng),來(lái)讓用戶自己選擇光標(biāo)位置。如果有什么好的解決方案, 請(qǐng)聯(lián)系我!!
(學(xué)習(xí)過程用, 遇到不太會(huì)的知識(shí), 盡量去找文檔, 翻Dash,也許會(huì)比直接baidu現(xiàn)成的代碼更好)
代碼
import UIKit
enum ImageAttachmentMode {
case Default //默認(rèn)(不改變大小)
case FitTextLine //使尺寸適應(yīng)行高
case FitTextView //使尺寸適應(yīng)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 //不設(shè)置是右
MessageTextView.addGestureRecognizer(swipeDownGesture)
}
fileprivate func insertPicture(_ image:UIImage,_ model:ImageAttachmentMode = .Default){
//將現(xiàn)存所有文字轉(zhuǎn)為 NSMutableAttributedString
let mutableString = NSMutableAttributedString.init()
mutableString.append(MessageTextView.attributedText)
//獲取光標(biāo)的位置
let mutableSelectRange = MessageTextView.selectedRange
//創(chuàng)建附件,來(lái)保存圖片
let imgAttachment = NSTextAttachment.init()
//用來(lái)保存附件 attachment轉(zhuǎn)化的attributeString
var imageAttributeString:NSAttributedString
//主要兩個(gè)屬性 一個(gè)image,圖片資源, 一個(gè)bounds 圖片位置
imgAttachment.image = image
//根據(jù)模式選擇圖片放置的模式
switch model {
//適應(yīng)文字大小
case .FitTextLine:
imgAttachment.bounds = CGRect.init(x: 0, y: -4, width: (MessageTextView.font?.lineHeight)!, height: (MessageTextView.font?.lineHeight)!)
//適應(yīng)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))
//記住新的光標(biāo)位置
let newSelectRange = NSMakeRange(mutableSelectRange.location+1,0)
//賦值到textview
MessageTextView.attributedText = mutableString
//刷新光標(biāo)
MessageTextView.selectedRange = newSelectRange
//滾動(dòng)到光標(biāo)位置
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)
//訪問相冊(cè)頁(yè)面
//從Dash文檔上根據(jù)文檔說明,步驟進(jìn)行開發(fā)
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:-數(shù)據(jù)源
//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
還在學(xué)習(xí)MJRefresh源碼中, 之后補(bǔ)上
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 其他內(nèi)容也相對(duì)簡(jiǎn)單,整合在一起記錄。
- 使用ViewModel的Array 作為tableview的數(shù)據(jù)源
- 查找tableview的delegate方法, 刪除,移動(dòng),根據(jù)操作修改ViewModel的Array數(shù)據(jù)
- 從SecondViewController中點(diǎn)擊Done 添加此數(shù)據(jù)到ViewModel的Array中。并且通過閉包回調(diào)到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
//回調(diào)方法
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:-數(shù)據(jù)源
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以后,可以直接使用這個(gè)屬性
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
}
}
在實(shí)際項(xiàng)目中實(shí)踐,在書寫記錄中鞏固,每日一記。