前段時(shí)間對(duì)手帳類 app 的實(shí)現(xiàn)細(xì)節(jié)非常感興趣,遂萌生了想自己實(shí)現(xiàn)一個(gè)最小化的可行性產(chǎn)品。當(dāng)然啦~既然是 MVP 模式下的產(chǎn)品,所以只實(shí)現(xiàn)了「功能」,但是在一些自己特別想要去「抄襲」的地方也下了一點(diǎn)功夫去追求 UI 的表現(xiàn)。
創(chuàng)新互聯(lián)專注于木壘哈薩克網(wǎng)站建設(shè)服務(wù)及定制,我們擁有豐富的企業(yè)做網(wǎng)站經(jīng)驗(yàn)。 熱誠(chéng)為您提供木壘哈薩克營(yíng)銷型網(wǎng)站建設(shè),木壘哈薩克網(wǎng)站制作、木壘哈薩克網(wǎng)頁(yè)設(shè)計(jì)、木壘哈薩克網(wǎng)站官網(wǎng)定制、成都小程序開(kāi)發(fā)服務(wù),打造木壘哈薩克網(wǎng)絡(luò)公司原創(chuàng)品牌,更為您提供木壘哈薩克網(wǎng)站排名全網(wǎng)營(yíng)銷落地服務(wù)。
小時(shí)候,我是一個(gè)手抄報(bào)愛(ài)好者,四年級(jí)的時(shí)候班里組織了一個(gè)手抄報(bào)比賽,老師要求每位同學(xué)利用周末的時(shí)間做一份手抄報(bào)進(jìn)行評(píng)比,主題自選。到現(xiàn)在我印象還非常深刻的是,我想了一個(gè)中午都不知道要選什么主題,在白紙上畫(huà)了一些東西后又全都擦掉了,弄臟了好幾張紙,最后畫(huà)出了一個(gè)地球,思路就慢慢打開(kāi)了。
到了周一交給老師的時(shí)候,我不敢第一個(gè)交,我排在了隊(duì)伍的最后。老師接到我的手抄報(bào)后,居然說(shuō):“來(lái)來(lái)來(lái),你們來(lái)看看什么叫手抄報(bào)”,我當(dāng)時(shí)的心率達(dá)到了極高點(diǎn),臉又紅又燙,站在老師身邊站也不是走也不是,尷尬的笑著,但內(nèi)心卻極度自豪。
到了初中,班主任也讓大家利用周末的時(shí)間去做了一個(gè)手抄報(bào),因?yàn)樵谛W(xué)的時(shí)候有了一點(diǎn)經(jīng)驗(yàn),再加上到了初中那會(huì)兒基本上使用計(jì)算機(jī)來(lái)輔助完成各種任務(wù)也都鋪開(kāi)了,我就尋思著能不能再做些創(chuàng)新。當(dāng)時(shí)柯達(dá)傳出了倒閉的消息,這相當(dāng)于是一代人的記憶吧~有時(shí)候我會(huì)跑到老房子里翻到各種膠卷,在陽(yáng)光的照射下看著映射出的反射圖像。
結(jié)合這個(gè)事件,我就想到了利用「膠卷」風(fēng)格的來(lái)闡述對(duì)保護(hù)鳥(niǎo)類的主題,從網(wǎng)上下載了一些各種鳥(niǎo)類的圖片,自己加工一下,終于把手抄報(bào)做好了交給老師。當(dāng)交給老師的那一刻,老師愉悅的笑了,并拿著我的手抄報(bào)在講臺(tái)上給同學(xué)們展示,“大家看下,做的還不錯(cuò)吧~嗯,挺好看!”。
高考完的那個(gè)暑假,《南國(guó)都市報(bào)》組織了一次中小學(xué)生手抄報(bào)大賽,當(dāng)時(shí)我用堂弟的身份參加這個(gè)大賽,拿了三等獎(jiǎng),獎(jiǎng)品是一張創(chuàng)新書(shū)店 500 元的購(gòu)書(shū)卡。
以上就是我對(duì)手抄報(bào)或者說(shuō)類似于手帳的這種手工畫(huà)的經(jīng)歷了,我特別喜歡這種講述一個(gè)故事的方式,可以很好的把我想要表達(dá)的東西通過(guò)一些文字、圖片和畫(huà)的方式展現(xiàn)出來(lái)。
所以,當(dāng)出現(xiàn)了手帳類 app 時(shí),我迅速的下載進(jìn)行使用,使用過(guò)程中確實(shí)達(dá)到了自己當(dāng)初通過(guò)組織一些元素和文字來(lái)講述一件事的初衷。前段時(shí)間突發(fā)奇想,如果我能自己做一個(gè)手帳,順便去探究實(shí)現(xiàn)一個(gè)手帳 app 中需要注意的問(wèn)題,那該多好??!
首先,我把 App Store 中「手帳」關(guān)鍵詞下的搜索排名前 10 的 app 都進(jìn)行了一番使用,總結(jié)出了一些手帳 app 通用點(diǎn):
添加文字。可旋轉(zhuǎn)、放大縮小、旋轉(zhuǎn)字體;
添加照片??尚D(zhuǎn)翻轉(zhuǎn)、放大縮小、并具備簡(jiǎn)單或者輔助的圖像修飾工具;
添加貼紙。使用一些繪制好的貼紙,操作與「添加照片」差不多;
模版。提供一套模版,用戶可以在這個(gè)模版規(guī)定好的區(qū)域進(jìn)行內(nèi)容添加;
提供無(wú)限長(zhǎng)或?qū)挼漠?huà)布。
基本上這些手帳 app 的功能就是這么多了,因?yàn)楸局?MVP 的思路去做這個(gè)項(xiàng)目,所以也就沒(méi)有做到高保真的設(shè)計(jì),直接抄了一個(gè)比較簡(jiǎn)潔的手帳 app 設(shè)計(jì)。
確定好了自己要實(shí)現(xiàn)的大概需要做的功能點(diǎn)后,就需要開(kāi)始去選擇技術(shù)棧,因?yàn)橐龅漠吘故?MVP 產(chǎn)品而不是 demo,我對(duì) demo 的理解是「實(shí)現(xiàn)某個(gè)功能點(diǎn)」,對(duì) MVP 產(chǎn)品的理解是「某個(gè)階段下的完整可用的產(chǎn)品」,MVP 模式下出來(lái)的東西細(xì)節(jié)出現(xiàn)一些問(wèn)題不用太過(guò)于苛責(zé),但整體的邏輯上一定是要完整的,不完整的邏輯可以沒(méi)有,但是一旦有了就要是完整的,覆蓋的邏輯路徑也可以不是 100%,但主邏輯一定要全覆蓋。
iOS APP開(kāi)發(fā)技術(shù)點(diǎn)如下:
純?cè)?Swift 開(kāi)發(fā);
網(wǎng)絡(luò)請(qǐng)求 =>?Alamofire
,一些簡(jiǎn)單的數(shù)據(jù)直接走?NSFileManager
?進(jìn)行文件持久化管理;
UI 組件全都基于?UIKit
?去做;社會(huì)化分享走系統(tǒng)分享,不集成其它 SDK;
模塊上提供「貼紙」、「畫(huà)筆」、「照片」和「文字」。做的過(guò)程中發(fā)現(xiàn)其實(shí)「照片」和「文字」本質(zhì)上來(lái)說(shuō)也是貼紙,省了不少事。
其實(shí)我對(duì)自己每新開(kāi)一個(gè) side project 都有一個(gè)硬性要求,做完后要對(duì)自己的技術(shù)水平有增長(zhǎng),其實(shí)「增長(zhǎng)」這個(gè)東西很玄學(xué),怎么定義「增長(zhǎng)」對(duì)吧?我給自己找到了一個(gè)最簡(jiǎn)單的思路:用新的東西去完成它!
因此在服務(wù)端上我就直接無(wú)腦的選擇了?Vapor
?進(jìn)行,通過(guò) Swift 去寫(xiě)服務(wù)端這是我之前一直想做但找不到時(shí)機(jī)去做的事情,借此機(jī)會(huì)就上車了。至于為什么不是選?Perfect
,其實(shí)我個(gè)人沒(méi)有去動(dòng)手實(shí)踐過(guò),只是聽(tīng)大佬們說(shuō)?Vapor
?的 API 風(fēng)格比較?Swifty
?一些。
在第一期的 MVP 中對(duì)服務(wù)端的依賴不大,所以目前的架構(gòu)比較簡(jiǎn)單,達(dá)到能用即可就完事了~關(guān)于?Vapor
?的一些使用細(xì)節(jié),可以在我的這篇文章中進(jìn)行查看,本文將不再細(xì)述?Vapor
?使用細(xì)節(jié)。
對(duì)于手帳來(lái)說(shuō),最核心的一個(gè)就是「貼紙」。如何把貼紙從存儲(chǔ)中拉出來(lái)放到畫(huà)布上,這一步解決了,后續(xù)大部分內(nèi)容也都解決了。
首先,我們需要明確一點(diǎn),在這個(gè)項(xiàng)目中,「畫(huà)布」本身也是個(gè)?UIView
,把「貼紙」添加到畫(huà)布上,實(shí)質(zhì)上就是把?UIImageView
?給?addSubview
?到?UIView
?上。其次,手帳中追求的是對(duì)素材的控制,可旋轉(zhuǎn)放大是基本操作,而且前文也說(shuō)過(guò)了,我們幾乎可以把「照片」和「文字」都認(rèn)為是對(duì)「貼紙」的繼承,所以這就抽離出了「貼紙」本身是所以可提供交互組件的基類。
手帳類 app 對(duì)貼紙進(jìn)行多手勢(shì)操作的流暢性是決定用戶留存率很大的一個(gè)因素。因此,我們?cè)俪殡x一下手帳「貼紙」,把基礎(chǔ)手勢(shì)操作都移到更高一層的父類中去,貼紙中留下業(yè)務(wù)邏輯。手勢(shì)操作核心代碼邏輯如下:
//?pinchGesture?縮放手勢(shì)//?縮放的方法(文件私有)。??gesture手勢(shì)?:UI縮放手勢(shì)識(shí)別器@objcfileprivate?func?pinchImage(gesture:?UIPinchGestureRecognizer)?{ ????//??當(dāng)前手勢(shì)?狀態(tài)???改變中 ????if?gesture.state?==?.changed?{ ????????//?當(dāng)前矩陣2D變換??縮放通過(guò)(手勢(shì)縮放的參數(shù)) ????????transform?=?transform.scaledBy(x:?gesture.scale,?y:?gesture.scale) ????????//?要復(fù)原到1(原尺寸),不要疊加放大 ????????gesture.scale?=?1 ????}}//?rotateGesture?旋轉(zhuǎn)手勢(shì)//?旋轉(zhuǎn)的方法(文件私有)。??gesture手勢(shì)?:UI旋轉(zhuǎn)手勢(shì)識(shí)別器@objcfileprivate?func?rotateImage(gesture:?UIRotationGestureRecognizer)?{ ????if?gesture.state?==?.changed?{ ????????transform?=?transform.rotated(by:?gesture.rotation) ????????//?0為弧度制(要跟角度轉(zhuǎn)換) ????????gesture.rotation?=?0 ????}}//?panGesture?拖拽/平移手勢(shì)//?平移的方法(文件私有)。??gesture手勢(shì)?:UI平移手勢(shì)識(shí)別器@objcfileprivate?func?panImage(gesture:?UIPanGestureRecognizer)?{ ????if?gesture.state?==?.changed?{ ????????//?坐標(biāo)轉(zhuǎn)換至父視圖坐標(biāo) ????????let?gesturePosition?=?gesture.translation(in:?superview) ????????//?用移動(dòng)距離與原位置坐標(biāo)計(jì)算。?gesturePosition.x?已經(jīng)帶正負(fù)了 ????????center?=?CGPoint(x:?center.x?+?gesturePosition.x,?y:?center.y?+?gesturePosition.y) ????????//?.zero?為?CGPoint(x:?0,?y:?0)的簡(jiǎn)寫(xiě),?位置坐標(biāo)回0 ????????gesture.setTranslation(.zero,?in:?superview) ????}}//?雙擊動(dòng)作(UI點(diǎn)擊手勢(shì)識(shí)別器)@objcfileprivate?func?doubleTapGesture(tap:?UITapGestureRecognizer)?{ ????//?狀態(tài)?雙擊結(jié)束后 ????if?tap.state?==?.ended?{ ????????//?翻轉(zhuǎn)?90度 ????????let?ratation?=?CGFloat(Double.pi?/?2.0) ????????//?變換???旋轉(zhuǎn)角度?=?之前的旋轉(zhuǎn)角度?+?旋轉(zhuǎn) ????????transform?=?CGAffineTransform(rotationAngle:?previousRotation?+?ratation) ????????previousRotation?+=?ratation????}}
實(shí)現(xiàn)的效果下圖所示:
使用?UICollectionView
?作為貼紙容器,通過(guò)閉包把點(diǎn)擊事件對(duì)應(yīng)索引映射的 icon 圖片實(shí)例化為貼紙對(duì)象傳遞給父視圖:
collectionView.cellSelected?=?{?cellIndex?in ????let?stickerImage?=?UIImage(named:?collectionView.iconTitle?+?"\(cellIndex)") ????let?sticker?=?UNStickerView() ????sticker.width?=?100 ????sticker.height?=?100 ????sticker.imgViewModel?=?UNStickerView.ImageStickerViewModel(image:?stickerImage!) ????self.sticker?(sticker)}
在父視圖中通過(guò)實(shí)現(xiàn)閉包接收貼紙對(duì)象,這樣就完成了「貼紙」到「畫(huà)布」的全流程。
stickerComponentView.sticker?=?{ ????$0.viewDelegate?=?self ????//?父視圖居中 ????$0.center?=?self.view.center ????$0.tag?=?self.stickerTag????self.stickerTag?+=?1 ????self.view.addSubview($0) ????//?添加到貼紙集合中 ????self.stickerViews.append($0)}
手帳編輯頁(yè)面的底部工具欄之前沒(méi)做好設(shè)計(jì),按道理來(lái)說(shuō),應(yīng)該直接上一個(gè)?UITabBar
?即可完事,但最終也使用了?UICollectionView
?完成。讀取設(shè)備照片操作比較簡(jiǎn)單,不需要自定義相冊(cè),所以通過(guò)系統(tǒng)的?UIImagePicker
?完成,對(duì)自定義相冊(cè)感興趣的同學(xué)可以看我的這篇文章。頂部工具欄的代碼細(xì)節(jié)如下所示:
//?底部的點(diǎn)擊事件collectionView.cellSelected?=?{?cellIndex?inswitch?cellIndex?{ ????//?背景 ????case?0: ????????self.stickerComponentView.isHidden?=?true ????????brushView.isHidden?=?true ????????self.bgImageView.image?=?brushView.drawImage() ????????self.present(self.colorBottomView,?animated:?true,?completion:?nil) ????//?貼紙 ????case?1: ????????brushView.isHidden?=?true ????????self.bgImageView.image?=?brushView.drawImage() ????????self.stickerComponentView.isHidden?=?false ????????UIView.animate(withDuration:?0.25,?animations:?{ ????????????self.stickerComponentView.bottom?=?self.bottomCollectionView!.y????????}) ????//?文字 ????case?2: ????????self.stickerComponentView.isHidden?=?true ????????brushView.isHidden?=?true ????????self.bgImageView.image?=?brushView.drawImage() ????????let?vc?=?UNTextViewController() ????????self.present(vc,?animated:?true,?completion:?nil) ????????vc.complateHandler?=?{?viewModel?in ????????????let?stickerLabel?=?UNStickerView(frame:?CGRect(x:?150,?y:?150,?width:?100,?height:?100)) ????????????self.view.addSubview(stickerLabel) ????????????stickerLabel.textViewModel?=?viewModel????????????self.stickerViews.append(stickerLabel) ????????} ????//?照片 ????case?3: ????????self.stickerComponentView.isHidden?=?true ????????brushView.isHidden?=?true ????????self.bgImageView.image?=?brushView.drawImage() ????????self.imagePicker.delegate?=?self ????????self.imagePicker.sourceType?=?.photoLibrary????????self.imagePicker.allowsEditing?=?true ????????self.present(self.imagePicker,?animated:?true,?completion:?nil) ????//?畫(huà)筆 ????case?4: ????????self.stickerComponentView.isHidden?=?true ????????brushView.isHidden?=?false ????????self.bgImageView.image?=?nil ????????self.view.bringSubviewToFront(brushView) ????default:?break}
底部工具欄的每一個(gè)模塊都是一個(gè)?UIView
,這部分做的也不太好,最佳的做法應(yīng)該是基于?UIWindow
?或者?UIViewController
?做一個(gè)「工具容器」作為各個(gè)模塊 UI 內(nèi)容元素的容器,通過(guò)這種做法就可以免去在底部工具欄的點(diǎn)擊事件回調(diào)中寫(xiě)這么多的視圖顯示 / 隱藏的狀態(tài)代碼。
關(guān)注「照片」部分的代碼塊,實(shí)現(xiàn)?UIImagePickerControllerDelegate
?協(xié)議后的方法為:
extension?UNContentViewController:?UIImagePickerControllerDelegate?{ ????///?從圖片選擇器中獲取選擇到的圖片 ????func?imagePickerController(_?picker:?UIImagePickerController, ???????????????????????????????didFinishPickingMediaWithInfo?info:?[UIImagePickerController.InfoKey?:?Any])?{ ????????//?獲取到編輯后的圖片 ????????let?image?=?info[UIImagePickerController.InfoKey.editedImage]?as??UIImage ????????if?image?!=?nil?{ ????????????let?wh?=?image!.size.width?/?image!.size.height????????????//?初始化貼紙 ????????????let?sticker?=?UNStickerView(frame:?CGRect(x:?150,?y:?150,?width:?100,?height:?100?*?wh)) ????????????//?添加視圖 ????????????self.view.addSubview(sticker) ????????????sticker.imgViewModel?=?UNStickerView.ImageStickerViewModel(image:?image!) ????????????//?添加到貼紙集合中 ????????????self.stickerViews.append(sticker) ????????????picker.dismiss(animated:?true,?completion:?nil) ????????} ????}}
文字模塊暴露給父視圖也是一個(gè)實(shí)例化后的貼紙對(duì)象,不過(guò)在文字 VC 里需要對(duì)文字進(jìn)行顏色、字體和字號(hào)的選擇。做完了才發(fā)現(xiàn)其實(shí)因?yàn)橘N紙是可以通過(guò)手勢(shì)進(jìn)行放大和縮小的,沒(méi)必要做字號(hào)的選擇......
其中比較費(fèi)勁的是對(duì)文字顏色的選擇,剛開(kāi)始我想的直接上 RGB 調(diào)色就算了,后來(lái)想到如果直接通過(guò) RGB 有三個(gè)通道,調(diào)起色來(lái)非常的難受。想到之前在做《瘋狂彈球》這個(gè)游戲時(shí)使用的 HSB 顏色模式,做一個(gè)圓盤(pán)顏色選擇器,后來(lái)在思考實(shí)現(xiàn)細(xì)節(jié)的過(guò)程中了這么 EF 寫(xiě)的這個(gè)庫(kù)?EFColorPicker,非常好用,改了改 UI 后直接拿來(lái)用了,感謝 EF !
「氣泡視圖」的本身是個(gè)?UIViewController
,但是需要對(duì)其幾個(gè)屬性進(jìn)行設(shè)置。其實(shí)現(xiàn)流程比較流程化,比較好的做法是封裝一下,把這些模版化的代碼變成一個(gè)「氣泡視圖」類供業(yè)務(wù)方使用,但因?yàn)闀r(shí)間關(guān)系就一直在 copy,核心代碼如下:
///?文字大小氣泡private?var?sizeBottomView:?UNBottomSizeViewController?{ ????get?{ ????????let?sizePopover?=?UNBottomSizeViewController() ????????sizePopover.size?=?self.textView.font?.pointSize ????????sizePopover.preferredContentSize?=?CGSize(width:?200,?height:?100) ????????sizePopover.modalPresentationStyle?=?.popover????????let?sizePopoverPVC?=?sizePopover.popoverPresentationController ????????sizePopoverPVC?.sourceView?=?self.bottomCollectionView ????????sizePopoverPVC?.sourceRect?=?CGRect(x:?bottomCollectionView!.cellCenterXs[1],?y:?0,?width:?0,?height:?0) ????????sizePopoverPVC?.permittedArrowDirections?=?.down ????????sizePopoverPVC?.delegate?=?self ????????sizePopoverPVC?.backgroundColor?=?.white ????????sizePopover.sizeChange?=?{?size?in ????????????self.textView.font?=?UIFont(name:?self.textView.font!.familyName,?size:?size) ????????} ????????return?sizePopover????}}
在需要彈出該氣泡視圖的地方通過(guò)?present
?即可調(diào)用:
collectionView.cellSelected?=?{?cellIndex?in ????switch?cellIndex?{ ????case?0:?self.present(self.fontBottomView, ????????????????????????????animated:?true, ????????????????????????????completion:?nil) ????case?1:?self.present(self.sizeBottomView, ????????????????????????????animated:?true, ????????????????????????????completion:?nil) ????case?2:?self.present(self.colorBottomView, ????????????????????????????animated:?true, ????????????????????????????completion:?nil) ????default:?break ????}}
之前在滴滴實(shí)習(xí)時(shí),寫(xiě)過(guò)一個(gè)關(guān)于畫(huà)筆的組件(居然已經(jīng)兩年前了...),但是這個(gè)畫(huà)筆是基于?drawRect:
?方法去做的,對(duì)于內(nèi)存十分不友好,一直畫(huà)下去,內(nèi)存就會(huì)一直漲,這回采用了?CAShapeLayer
?重寫(xiě)了一個(gè),效果還不錯(cuò)。
關(guān)于畫(huà)筆的撤回之前基于?drawRect:
?的方式去做就會(huì)非常簡(jiǎn)單,每一次的撤回相當(dāng)于重繪一次,把被撤回的線從繪制點(diǎn)數(shù)組中?remove
?掉就好了,但基于?CAShapeLayer
?實(shí)現(xiàn)不太一樣,因?yàn)槠涿恳还P都是直接生成在?layer
?中了,如果需要撤回就得把當(dāng)前重新生成?layer
。
所以最后我的做法是每畫(huà)一筆都去生成一張圖片保存到數(shù)組中,當(dāng)執(zhí)行撤回操作時(shí),就把撤回?cái)?shù)組中的最后一個(gè)元素替換當(dāng)前正在的繪制畫(huà)布內(nèi)容,并從撤回?cái)?shù)組中移除這個(gè)元素。
有了撤回,那也要把重做給上了。重做的就是防止撤回,做法跟撤回類似。再創(chuàng)建一個(gè)重做數(shù)組,把每次從撤回?cái)?shù)組中移除掉的圖片都?append
?到重做數(shù)組中即可。以下為撤回重做的核心代碼:
//?undo?撤回@objcprivate?func?undo()?{ ????//?undoDatas?可撤回集合?數(shù)量 ????guard?undoDatas.count?!=?0?else?{?return?} ????//?如果是撤回集合中只有?1?個(gè)數(shù)據(jù),則說(shuō)明撤回后為空 ????if?undoDatas.count?==?1?{ ????????//?重做?redo??append?添加 ????????redoDatas.append(undoDatas.last!) ????????//?撤回?undo?清空 ????????undoDatas.removeLast() ????????//?清空?qǐng)D片視圖 ????????bgView.image?=?nil ????}?else?{ ????????//?把?3?給?redo ????????redoDatas.append(undoDatas.last!) ????????//?從?undo?移除?3.?還剩?2?1 ????????undoDatas.removeLast() ????????//?清空?qǐng)D片視圖 ????????bgView.image?=?nil ????????//?把?2?給圖片視圖 ????????bgView.image?=?UIImage(data:?undoDatas.last!) ????}}//?redo?重做@objcprivate?func?redo()?{ ????if?redoDatas.count?>?0?{ ????????//?先賦值,再移除(redo的last給圖片視圖) ????????bgView.image?=?UIImage(data:?redoDatas.last!) ????????//?redo的last?給?undo撤回?cái)?shù)組 ????????undoDatas.append(redoDatas.last!) ????????//?從redo重做?移除last ????????redoDatas.removeLast() ????}}
關(guān)于橡皮的思路我是這么考慮的。按照現(xiàn)實(shí)生活中情況,使用橡皮時(shí)是把已經(jīng)寫(xiě)在紙上的筆跡給擦除,換到項(xiàng)目中來(lái)看,其實(shí)橡皮也是一種畫(huà)筆只不過(guò)是沒(méi)有顏色的畫(huà)筆罷了,并且可以有兩種思路:
筆跡直接加在?contentLayer
?上,此時(shí)需要對(duì)橡皮做一個(gè)?mask
,把橡皮筆跡的路徑和底圖做一個(gè)?mask
,這樣橡皮筆跡留下的內(nèi)容就是底圖的內(nèi)容了;
筆跡加在另外一個(gè)?layer
?上。這種情況可以直接給橡皮設(shè)置成該?layer
?的背景色,相當(dāng)于?clearColor
。
第二種做法我沒(méi)試過(guò),但是第一種做法是非常 OK 的。
以上就是手帳 app 的最小可行性產(chǎn)品了,當(dāng)然還有很多細(xì)節(jié)都沒(méi)有展開(kāi),比如服務(wù)端部分的代碼思路。因?yàn)榉?wù)端還是圍繞產(chǎn)品出發(fā),設(shè)計(jì)上也不太好,是我第一次使用?Vapor
?進(jìn)行開(kāi)發(fā),只發(fā)揮出了?Vapor
?的 10% 功力。目前服務(wù)端完成的需求有:
用戶的登錄注冊(cè)和鑒權(quán);
手帳及手帳本的創(chuàng)建、刪除和修改;
貼紙的創(chuàng)建、刪除和修改。
如果不想與服務(wù)端進(jìn)行交互,可以直接該對(duì)應(yīng)按鈕的點(diǎn)擊事件為你想要展示的類,并注釋掉對(duì)應(yīng)的服務(wù)端代碼即可。
分享文章:如何實(shí)現(xiàn)一個(gè)手帳APP開(kāi)發(fā)
新聞來(lái)源:http://aaarwkj.com/article32/pegosc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供定制網(wǎng)站、網(wǎng)站制作、做網(wǎng)站、企業(yè)網(wǎng)站制作、App設(shè)計(jì)、商城網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)