原文可在我博客上看,會(huì)有目錄結(jié)構(gòu):http://data2art.com/iOSDevRecord.html
半個(gè)月前,完成了個(gè)人App的2.0版本,也在普天同慶的六一兒童節(jié)這天上架了。因?yàn)槭莻€(gè)人開發(fā),很多實(shí)現(xiàn)都是邊探索邊做。現(xiàn)在完成之后再回顧,發(fā)現(xiàn)自己走了些彎路。所以寫了這篇總結(jié),概覽了從想法、設(shè)計(jì)、開發(fā)到最終發(fā)布的過(guò)程。希望讀者參考本文,可以少走一些彎路;另外,本文也給列出了開發(fā)中具體思路和資源列表。
知識(shí)從何而來(lái)?
Apple的知識(shí)又是從何而來(lái)?是哪些人在創(chuàng)造這些機(jī)制,又是哪些人在傳播這些機(jī)制?為什么要這樣設(shè)計(jì)呢?為什么要這樣編碼呢?
iOS開發(fā)是在詢問(wèn)什么問(wèn)題?技術(shù)的實(shí)現(xiàn),究竟是在問(wèn)什么?為什么要這樣做?那樣做?評(píng)價(jià)的標(biāo)準(zhǔn)為何?
資源
我在開發(fā)過(guò)程中常使用的資源:
- 相關(guān)書籍
尋找大致實(shí)現(xiàn)方向,我有龐大的電子書庫(kù),在此感謝學(xué)校提供的優(yōu)質(zhì)資源。很多書,都會(huì)先檢視閱讀一遍,這樣心中有地圖,開發(fā)時(shí)就可快速定位。
書本是理論的簡(jiǎn)單系統(tǒng)化表示。
- Apple 官方文檔 視頻 示例代碼
系統(tǒng)化的概覽,具體可使用內(nèi)容的查找。
- StackOverflow
主要是查找一些細(xì)節(jié)問(wèn)題。
- Github上的相關(guān)項(xiàng)目
看具體的代碼實(shí)現(xiàn),分析不同實(shí)現(xiàn)的優(yōu)缺點(diǎn),取其精華去其糟粕。
- Raywenderlich
可以很快上手入門新知識(shí)點(diǎn)。
- 博客
- 論文
對(duì)于想深入理解的知識(shí),會(huì)參考相關(guān)論文。
搜索時(shí)使用google或者bing,絕對(duì)可以節(jié)約你的時(shí)間。時(shí)間即是生命。
我選擇的簡(jiǎn)單是易用SS,我的推介鏈接。
想法
- 目標(biāo):簡(jiǎn)潔優(yōu)雅易用節(jié)拍器
- 用戶:學(xué)習(xí)樂(lè)器演奏的群體
- 使用場(chǎng)景:樂(lè)器演奏
- 做什么:小而美。好看,好用,占內(nèi)存小。
- 不做什么:不做專業(yè)程度極高,功能十分完備的節(jié)拍器。
設(shè)計(jì)
我一般使用Sketch進(jìn)行快速原型設(shè)計(jì)。
同時(shí)思考,是否可實(shí)現(xiàn)?
開發(fā)
我使用的是coding的倉(cāng)庫(kù),git進(jìn)行版本管理。
主要介紹2.0版本中的一些開發(fā)過(guò)程。
你可以免費(fèi)下載,看看有哪些基本功能。
多主題設(shè)計(jì)
配色
參考網(wǎng)站:
- http://www.rocket-design.fr/color-template/
- http://www.shejidaren.com/examples/tools/color-scheme/
- http://www.shejidaren.com/mbe-style.html
- http://www.peise.net/
扁平化顏色庫(kù):
Chameleon
is a lightweight, yet powerful, color framework for iOS (Objective-C & Swift). It is built on the idea that software applications should function effortlessly while simultaneously maintaining their beautiful interfaces.
多主題實(shí)現(xiàn)
OC版:
https://github.com/Draveness/DKNightVersion
Swift版:
http://www.lxweimin.com/p/a5cd0176bcf5
https://github.com/zhangbozhb/ChameleonSwift
- theme
- view
部分配色表:
Name | defaultColor | SeaColor | GreenColor | CoffeeColor|
------- | -------| ------- | ------- | ------- | ------- |
backgroundColor| | | | |
BlockColor | rgba(184, 184, 184, 1)| #D5EBE9|#F4ADA2|#D4C38F|
BlockColorFill| rgba(251, 251, 251, 1)|#F5FAF9|#F07973|#EFDFAF|
BlockBdrColor| rgba(57, 57, 57, 1)|#38465F|#38465F|#272727|
shadowColor| rgba(41, 44, 48, 1)|#38465F|#A0785C|#5D4531|
Sliderstart| rgba(184, 184, 184, 1)|#F5FAF9|#F2F2F2|#88DEF2|
Sliderend| rgba(185, 200, 245, 1)|#B0D5C2|#F4ADA2|#FAD199|
SliderBackground| Black|#364960|#3A4C39|#2B2B2B|
nameIncDecTextColor|Black|White|Black|Black|
labelColor|Black|white|Black|Black|
根據(jù)圖片配色舉例:
代碼實(shí)現(xiàn):
- Struct方式
public protocol YXYTheme {
// MetreView
var blockColor : UIColor { get set }
var blockFillColor : UIColor { get set }
var blockBdrColor : UIColor { get set }
var blockShadowColor : UIColor { get set }
// View
var backgroundColor : UIColor { get set }
var nameIncDecTextColor : UIColor { get set }
var labelColor : UIColor { get set }
var incAndDecLabelTextColor : UIColor { get set }
// Slider
var sliderBackgroundColor : UIColor { get set }
var sliderStartColor : UIColor { get set }
var sliderEndColor : UIColor { get set }
var sliderHandleColor : UIColor { get set }
}
struct DarkTheme : YXYTheme, AnyObjectConvertible {
var blockColor = UIColor(red:0.72, green:0.72, blue:0.72, alpha:1)
var blockFillColor = UIColor(red:0.95, green:0.95, blue:0.95, alpha:1)
var blockBdrColor = UIColor(red:0.21, green:0.21, blue:0.21, alpha:1)
var blockShadowColor = UIColor(red:0.16, green:0.17, blue:0.19, alpha:1)
var backgroundColor = UIColor(red:0.34, green:0.34, blue:0.34, alpha:1)
var nameIncDecTextColor = UIColor(red:0.95, green:0.95, blue:0.95, alpha:1)
var labelColor = UIColor.whiteColor()
var incAndDecLabelTextColor = UIColor.whiteColor()
var sliderBackgroundColor = UIColor.lightGrayColor()
var sliderStartColor = UIColor.grayColor()
var sliderEndColor = UIColor.greenColor()
var sliderHandleColor = UIColor.greenColor()
}
- class 方式
//
// LightTheme.swift
// GuitarFere
//
// Created by youxinyu on 16/3/10.
// Copyright ? 2016年 yogayu.github.io. All rights reserved.
//
import UIKit
class LightTheme : NSObject, YXYTheme, AnyObjectConvertible
{
var blockColor = UIColor(red:0.72, green:0.72, blue:0.72, alpha:1)
var blockFillColor = UIColor(red:0.95, green:0.95, blue:0.95, alpha:1)
var blockBdrColor = UIColor(red:0.21, green:0.21, blue:0.21, alpha:1)
var blockShadowColor = UIColor(red:0.16, green:0.17, blue:0.19, alpha:1)
var backgroundColor = UIColor.whiteColor()
var nameIncDecTextColor = UIColor(red:0.95, green:0.95, blue:0.95, alpha:1)
var labelColor = UIColor.whiteColor()
var incAndDecLabelTextColor = UIColor.whiteColor()
var sliderBackgroundColor = UIColor.lightGrayColor()
var sliderStartColor = UIColor.grayColor()
var sliderEndColor = UIColor.redColor()
var sliderHandleColor = UIColor.greenColor()
init( blockColor:UIColor, blockFillColor:UIColor, blockBdrColor:UIColor, blockShadowColor:UIColor,
backgroundColor:UIColor, nameIncDecTextColor:UIColor, labelColor:UIColor, incAndDecLabelTextColor:UIColor,
sliderBackgroundColor:UIColor, sliderStartColor:UIColor, sliderEndColor:UIColor, sliderHandleColor:UIColor){
self.blockColor = blockColor
self.blockFillColor = blockFillColor
self.blockBdrColor = blockBdrColor
self.blockShadowColor = blockShadowColor
self.backgroundColor = backgroundColor
self.nameIncDecTextColor = nameIncDecTextColor
self.labelColor = labelColor
self.incAndDecLabelTextColor = incAndDecLabelTextColor
self.sliderBackgroundColor = sliderBackgroundColor
self.sliderStartColor = sliderStartColor
self.sliderEndColor = sliderEndColor
self.sliderHandleColor = sliderHandleColor
}
}
本地化
參見(jiàn)我之前博文:
http://www.lxweimin.com/p/782aaf3bf7da
或
http://azureyu.com/2016-05-14-iOS-Localizable.html
保持用戶設(shè)置
let userDefaultsLastTempoKey = "DefaultsTempoKey"
let defaults = NSUserDefaults.standardUserDefaults()
func saveTempo(tempo:Int){
defaults.setInteger(tempo, forKey: userDefaultsLastTempoKey)
defaults.synchronize()
}
讀取:
func initialTempo(){
let savedTempo = NSUserDefaults.standardUserDefaults().objectForKey( userDefaultsLastTempoKey) as? Int
if let tempo = savedTempo {
metronome.tempo = tempo
tempoLabel.text = "\(metronome.tempo)"
}else {
tempoLabel.text = "\(metronome.tempo)"
}
Struct如何轉(zhuǎn)為AnyObject?
參考:
https://github.com/tarunon/AnyObjectConvertible
class Box<T> {
let value: T
init(value: T) {
self.value = value
}
}
NSNotificationCenter.defaultCenter().postNotificationName("foo", object: Box(value: YourOwnStruct())) // OK
But Box<T> unwrap is too lazy.
let value = (notification.object as? Box<YourOwnStruct>)?.value
You can cast your struct/enum directory if implement AnyObjectConvertible at that type.
extension YourOwnStruct: AnyObjectConvertible {}
NSNotificationCenter.defaultCenter().postNotificationName("foo", object: YourOwnStruct()) // OK
let value = notification.object as? YourOw
存儲(chǔ)用戶當(dāng)前主題設(shè)置
初始顯示,無(wú)法使用函數(shù)更改,為什么?
解決:因?yàn)榇娴膬?nèi)容不對(duì),主題是一個(gè)Struct或Class。
func initTheme() {
let savedTheme = retrieveTheme()
if let theme = savedTheme {
UIApplication.ch_switchTheme(lightTheme)
}else{
// ...
}
}
轉(zhuǎn)化Struct為AnyObject之后存儲(chǔ):AnyObject, BOX(Theme)
取:AnyObject。傳給UIApplication.ch_switchTheme(theme)的是YXYTheme,需要將AnyObject轉(zhuǎn)為YXYTheme。
GuitarFere[20074:736310] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to insert non-property list object GuitarFere.Box<GuitarFere.YXYTheme> for key DefaultThemeKey'
The code you posted tries to save an array of custom objects to NSUserDefaults. You can't do that. Implementing the NSCoding methods doesn't help. You can only store things like NSArray, NSDictionary, NSString, NSData, NSNumber, and NSDate in NSUserDefaults.
You need to convert the object to NSData (like you have in some of the code) and store that NSData in NSUserDefaults. You can even store an NSArray of NSData if you need to.
When you read back the array you need to unarchive the NSData to get back your BC_Personobjects.
http://stackoverflow.com/questions/19720611/attempt-to-set-a-non-property-list-object-as-an-nsuserdefaults
func saveTheme(theme:LightTheme){
// let archivedObject = NSKeyedArchiver.archivedDataWithRootObject((theme as? NSObject)!)
let archivedObject = NSKeyedArchiver.archiveRootObject(theme as NSObject, toFile: userDefaultsLastThemeKey)
defaults.setObject(archivedObject, forKey: userDefaultsLastThemeKey)
defaults.synchronize()
}
存:
func saveTheme(theme:LightTheme){
// let archivedObject = NSKeyedArchiver.archivedDataWithRootObject((theme as? NSObject)!)
let archivedObject = NSKeyedArchiver.archiveRootObject(theme as NSObject, toFile: userDefaultsLastThemeKey)
defaults.setObject(archivedObject, forKey: userDefaultsLastThemeKey)
defaults.synchronize()
}
上面解決方式還是有問(wèn)題。
突然想到,不用保存主題本身,直接保存是第幾個(gè)(Int)主題就好。
問(wèn)題就這樣解決了。
搖一搖換膚
override func canBecomeFirstResponder() -> Bool {
return true
}
override func motionBegan(motion: UIEventSubtype, withEvent event: UIEvent?) {
if(event?.subtype == UIEventSubtype.MotionShake) {
randomTheme()
print("shacked")
self.setNeedsStatusBarAppearanceUpdate()
}
}
func randomTheme() {
let max = themes.count - 1
let index = randomIn(min: 0, max: max)
let randomTheme = themes[index]
saveTheme(index)
UIApplication.ch_switchTheme(randomTheme)
}
鐘擺繪制
主要使用圖像繪制。
例如繪制三角形:
func drawTriangle() {
//1.獲得圖形上下文
let context = UIGraphicsGetCurrentContext()
//繪制三角形
let height = self.frame.height
let width = self.frame.width
CGContextMoveToPoint(context, 0, 0)
CGContextAddLineToPoint(context, width, height/2)
CGContextAddLineToPoint(context, 0, height)
//關(guān)閉路徑,閉環(huán),(連接起點(diǎn)和最后一個(gè)點(diǎn))
powerOffColor.setFill()
CGContextClosePath(context)
//顯示在view上
CGContextFillPath(context)
}
側(cè)邊菜單欄
查看Github上相關(guān)實(shí)現(xiàn),一開始選擇的是SlideMenuControllerSwift,后來(lái)決定更改為自定義,使用更簡(jiǎn)潔的方式。
分離
分離之前的SliderMeanController,再添加動(dòng)畫。
- MainViewController
remove:
extension MainViewController:SlideMenuControllerDelegate{
func leftWillOpen() {
print("SlideMenuControllerDelegate: leftWillOpen")
OnceOpened = true
}
func leftDidOpen() {
print("SlideMenuControllerDelegate: leftDidOpen")
}
func leftWillClose() {
print("SlideMenuControllerDelegate: leftWillClose")
noteLabel.text = "\(metronome.noteNum)"
metreLabel.text = "\(metronome.metreView.numMetre)"
tempoLabel.text = "\(metronome.tempo)"
tempoItalianName(italianName)
initialHandelPoint()
metronome.metreView.setNeedsDisplay()
print("subview count:")
print(view.subviews.count)
self.ball.setNeedsDisplay()
}
func leftDidClose() {
print("SlideMenuControllerDelegate: leftDidClose")
}
}
- LeftViewController
remove:
wiilappear:
initialMenu()
class里面:
weak var delegate: LeftMenuProtocol?
func initialMenu() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let nonMenuController = storyboard.instantiateViewControllerWithIdentifier("purchaseViewController") as! PurchaseViewController
nonMenuController.delegate = self
self.nonMenuViewController = UINavigationController(rootViewController: nonMenuController)
}
class 前:
enum LeftMenu: Int {
case Main = 0
}
protocol LeftMenuProtocol : class {
func changeViewController(menu: LeftMenu)
}
class extension:
// MARK: - LeftMenuProtocol
extension LeftViewController: LeftMenuProtocol{
func changeViewController(menu: LeftMenu) {
switch menu {
case .Main:
self.slideMenuController()?.changeMainViewController(self.mainViewController, close: true)
}
}
}
alert 轉(zhuǎn)場(chǎng):
self.slideMenuController()?.
changeMainViewController(self.nonMenuViewController, close: true)
App delegate里面:
private func createMenuView() {
// create viewController code...
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let mainViewController = storyboard.instantiateViewControllerWithIdentifier("MainViewController") as! MainViewController
let leftViewController = storyboard.instantiateViewControllerWithIdentifier("LeftViewController") as! LeftViewController
let mvc: UINavigationController = UINavigationController(rootViewController: mainViewController)
UINavigationBar.appearance().tintColor = UIColor(hex: "689F38")
leftViewController.mainViewController = mvc
let slideMenuController = ExSlideMenuController(mainViewController:mvc, leftMenuViewController: leftViewController)
slideMenuController.automaticallyAdjustsScrollViewInsets = true
slideMenuController.delegate = mainViewController
// self.window?.backgroundColor = UIColor(red: 236.0, green: 238.0, blue: 241.0, alpha: 1.0)
self.window?.rootViewController = slideMenuController
self.window?.makeKeyAndVisible()
}
purchaseViewCont:
class 里面:
weak var delegate: LeftMenuProtocol?
func done() {
delegate?.changeViewController(LeftMenu.Main)
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
self.removeNavigationBarItem()
let doneTitle = NSLocalizedString("doneTitle", comment: "Purchase done title")
let rightButton: UIBarButtonItem = UIBarButtonItem(title: doneTitle, style: .Plain, target: self, action: #selector(done))
navigationItem.rightBarButtonItem = rightButton
動(dòng)畫Spring Animation
我使用的是MengTo的Spring動(dòng)畫庫(kù)。
內(nèi)購(gòu)
技術(shù)參考:
https://developer.apple.com/in-app-purchase/
https://www.raywenderlich.com/122144/in-app-purchase-tutorial
https://www.raywenderlich.com/121218/video-tutorial-in-app-purchase-series-introduction
https://github.com/mattt/Ono
https://github.com/awseeley/Swift-In-App-Purchase-Tutorial
頁(yè)面實(shí)現(xiàn):
How to make a beautiful page for the purchase?
使用Collection View,使用卡片展示。
聲音
Where to find the good sound?
推薦網(wǎng)站:
- https://www.freesound.org/people/toiletrolltube/sounds/345691/
- http://www.findsounds.com/ISAPI/search.dll?keywords=drum+solo
聲音下載之后需要自己進(jìn)行一些細(xì)化處理,推薦Sound Studio,它小而簡(jiǎn)潔,進(jìn)行簡(jiǎn)單的處理足夠了。
后臺(tái)播放
參考書籍:iOS8 Programming
Appledelegate:
func application(application: UIApplication,didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// paly on the background
_ = try? AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryAmbient, withOptions: [])
// others
}
func applicationWillResignActive(application: UIApplication) {
_ = try? AVAudioSession.sharedInstance().setActive(true, withOptions: [])
}
func applicationDidBecomeActive(application: UIApplication) {
_ = try? AVAudioSession.sharedInstance().setActive(true, withOptions: [])
}
細(xì)節(jié)問(wèn)題
問(wèn)題:
為什么nav顏色無(wú)法更改,感覺(jué)蒙上了一層影?
解決:
參考:
- Swift: https://github.com/DanisFabric/RainbowNavigation
p.p1 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px 'Helvetica Neue'; color: #999999}p.p2 {margin: 0.0px 0.0px 0.0px 60.0px; font: 16.0px 'Helvetica Neue'; color: #999999; min-height: 18.0px}span.s1 {font: 16.0px 'PingFang SC'}span.Apple-tab-span {white-space:pre}
Swift: https://github.com/DanisFabric/RainbowNavigation
//
// UINavigationBarExtension.swift
// GuitarFere
//
// Created by youxinyu on 16/5/9.
// Copyright ? 2016年 yogayu.github.io. All rights reserved.
//
import UIKit
private var kBackgroundViewKey = "kBackgroundViewKey"
private var kStatusBarMaskKey = "kStatusBarMaskKey"
extension UINavigationBar {
public func df_setStatusBarMaskColor(color: UIColor) {
if statusBarMask == nil {
statusBarMask = UIView(frame: CGRect(x: 0, y: -20, width: UIScreen.mainScreen().bounds.width, height: 20))
statusBarMask?.autoresizingMask = [.FlexibleWidth,.FlexibleHeight]
if let tempBackgroundView = backgroundView {
insertSubview(statusBarMask!, aboveSubview: tempBackgroundView)
}else {
insertSubview(statusBarMask!, atIndex: 0)
}
}
statusBarMask?.backgroundColor = color
}
public func df_setBackgroundColor(color: UIColor) {
if backgroundView == nil {
setBackgroundImage(UIImage(), forBarMetrics: UIBarMetrics.Default)
shadowImage = UIImage()
backgroundView = UIView(frame: CGRect(x: 0, y: -20, width: UIScreen.mainScreen().bounds.width, height: 64))
backgroundView?.userInteractionEnabled = false
backgroundView?.autoresizingMask = [.FlexibleHeight,.FlexibleWidth]
insertSubview(backgroundView!, atIndex: 0)
}
backgroundView?.backgroundColor = color
}
public func df_reset() {
setBackgroundImage(nil, forBarMetrics: .Default)
shadowImage = nil
backgroundView?.removeFromSuperview()
backgroundView = nil
}
// MARK: Properties
private var backgroundView:UIView? {
get {
return objc_getAssociatedObject(self, &kBackgroundViewKey) as? UIView
}
set {
objc_setAssociatedObject(self, &kBackgroundViewKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
private var statusBarMask:UIView? {
get {
return objc_getAssociatedObject(self, &kStatusBarMaskKey) as? UIView
}
set {
objc_setAssociatedObject(self, &kStatusBarMaskKey, newValue, .OBJC_ASSOCIATION_RETAIN)
}
}
}
在MainViewController中添加:
self.navigationController?.navigationBar.df_setBackgroundColor(UIColor.clearColor())
為什么點(diǎn)擊按鈕之后,圖片位置會(huì)改變?
改變UIButton的image之后,它的位置也會(huì)改變,需要將之前的先存儲(chǔ),改變圖片之后再賦給它。
CGPoint currentLoc = self.imageButton.center;
[self.imageButton setImage:[UIImage imageNamed:@"face"] forState:UIControlStateNormal];
self.imageButton.center = currentLoc;
好像不是這個(gè)問(wèn)題。我把外面的View去掉一層就OK了。
UIScrollerView
UIScrollerView的contentSize是取決于其子視圖的,所以一定要通過(guò)子視圖來(lái)限制其大小。
UIScrollerView需要探索的地方還很多,比如像相冊(cè)這樣的應(yīng)用,是兩個(gè)scrollerView,一個(gè)用來(lái)zoom,一個(gè)用來(lái)左右切換。
測(cè)試
- TestFlight測(cè)試 (外部測(cè)試需審核)
- 其他第三方測(cè)試 (無(wú)需審核)
發(fā)布
- 如何取好App名字?
- 如何寫好App介紹?
- 制作App簡(jiǎn)短視頻?
網(wǎng)站
因?yàn)橐沧鲞^(guò)一些網(wǎng)站,用Bootstrap寫過(guò)前端,PHP寫過(guò)后臺(tái)。基本的HTML/CSS,JS都會(huì)些,所以做網(wǎng)站對(duì)我來(lái)說(shuō)沒(méi)什么問(wèn)題。不過(guò),你不需要那么多知識(shí),你可以在直接使用模板,再進(jìn)行修改即可。
- 選擇模板
- 準(zhǔn)備內(nèi)容素材(圖片、文字、鏈接)
截圖
素材:
- 在設(shè)備上運(yùn)行,同時(shí)按home+電源鍵進(jìn)行截圖
- 或者使用模擬器運(yùn)行之后按Command+S,即可保持截圖
AppStore介紹截圖制作:
- 使用Sketch
- 推薦模板:https://github.com/LaunchKit/SketchToAppStore
- 思考介紹內(nèi)容,編輯,修改,再修改,再修改
- 導(dǎo)出
視頻
錄制步驟:
- 連接設(shè)備
- 打開QuickTime Player
- 進(jìn)行文件影片錄制
- 使用iMovie進(jìn)行剪輯,iMove中可直接新建應(yīng)用商店預(yù)覽視頻。
Tips:
- 視頻上傳需使用Safair瀏覽器,最好用iMovie中直接選擇導(dǎo)出為應(yīng)用商店預(yù)覽視頻。這樣不會(huì)出現(xiàn)視屏幀數(shù)太多等問(wèn)題。
- 如何旋轉(zhuǎn)視頻?使用QuickTime Player打開,然后在菜單中選擇編輯,向左選擇即可。
最好將所有素材放在同一個(gè)文件夾中,按照一定的命名方式進(jìn)行整理。
上傳
- https://developer.apple.com/app-store/cn/
- https://itunespartner.apple.com/cn/apps/videos
- https://app.grammarly.com/ 避免英語(yǔ)文法錯(cuò)誤
介紹
English:
Pulse is a clean and beautiful Metronome. It helps you better your music feeling and skill. With Pulse, your play time will be much more joyful.
Features:
- Colorful Themes. There are ten attractive themes that you can choose: night, tree, coffee, pink, azure, blue, purple...... make your play time more colorful.
- Nice Sounds. You can hear the different kinds of sounds: wood, ping, claves, triangle, shaker, blocks......choice the one suit your ear.
- Save setlist. You can save the setlist that you often play, it's easy to use.
- Swing. You can visualize the time passing, see the movements. In Pulse, there are 7 swing types: none, small, medium, large, ball, square, diamond. It always has the one you want.
Others:
- Play on the lock mood and background.
- Universal app, available on you iPod touch、iPhone and iPad.
Support :
- E-mail: yxydiscovery@gmail.com
- Website: http://azureyu.com/pulse
- Twitter: https://twitter.com/yxydiscovery
- Weibo: http://weibo.com/yxydiscovery
中文:
律動(dòng)是一款簡(jiǎn)潔而美觀的節(jié)拍器。它能夠幫助你提升樂(lè)感和技能。繽紛的主題,悅耳的音色,可視化時(shí)間流逝的鐘擺都能讓你的練習(xí)更為多彩。
特點(diǎn):
- 十種主題繽紛主題任你選擇:碳黑、咖啡、森林、粉紅、蔚藍(lán)、紫藤、翠綠等。
- 十余種悅耳音色舒適雙耳:實(shí)木、沙鈴、三角鐵、鈴環(huán)、木魚、鼓、鋼琴、鐵、鐺等。
- 一鍵保存演出列表:一鍵保持你的演出列表,節(jié)約你的時(shí)間,方便你的練習(xí)。
- 7種鐘擺模式:無(wú), 小, 中, 長(zhǎng), 球, 方, 菱。可視化時(shí)間流逝的最佳選擇。
其他:
- 支持鎖屏播放和后臺(tái)播放
- 支持屏幕常量
- 支持所有iPod Touch、iPhone和iPad設(shè)備
反饋:
- E-mail: yxydiscovery@gmail.com
- Website: http://azureyu.com/pulse
- Twitter: https://twitter.com/yxydiscovery
- Weibo: http://weibo.com/yxydiscovery
被拒5-24
版本上傳錯(cuò)誤。
再次被拒
Apple審核團(tuán)隊(duì)說(shuō)App會(huì)在iPad Air下點(diǎn)擊菜單按鈕會(huì)crash,可是測(cè)試了很多次之后,我都沒(méi)能重現(xiàn)crash,和他們溝通無(wú)果。等了兩天,我在代碼原封不動(dòng)的情況下,重新build了一個(gè)版本,再上傳,就通過(guò)了。
審核通過(guò) 6-1
Market
- 產(chǎn)品推薦網(wǎng)站 :例如36NEXT,MindStore之類。
- Weibo Twitter BBS
用戶會(huì)去哪些地方?
麻雀雖小,五臟俱全。雖只是一個(gè)簡(jiǎn)單的節(jié)拍器,也沒(méi)用到復(fù)雜的算法和很難的技術(shù)。
但學(xué)習(xí)本就是從易到難的吧。重要的是有想法并去實(shí)現(xiàn),然后不斷去完善。
作者「AzureYu」于 2016/6/2 更新本文
文章聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名 | BY-NC-SA