iOS 獨(dú)立開發(fā)記錄

原文可在我博客上看,會(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)站:

扁平化顏色庫(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.

https://github.com/ViccAlexander/Chameleon

多主題實(shí)現(xiàn)

OC版:
https://github.com/Draveness/DKNightVersion

Swift版:
http://www.lxweimin.com/p/a5cd0176bcf5
https://github.com/zhangbozhb/ChameleonSwift

  1. theme
  2. 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ù)圖片配色舉例:


Color_Sail___Design_Seeds.png

seaColor:

Color1.png

代碼實(shí)現(xiàn):

  1. 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()
}
  1. 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)畫。

  1. 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")
   }  
   }
  1. 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)站:

聲音下載之后需要自己進(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é)蒙上了一層影?

nav_problem.png

解決:
參考:

  • 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ā)布

  1. 如何取好App名字?
  2. 如何寫好App介紹?
  3. 制作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)容素材(圖片、文字、鏈接)

最終效果:http://azureyu.com/pulse

截圖

素材:

  • 在設(shè)備上運(yùn)行,同時(shí)按home+電源鍵進(jìn)行截圖
  • 或者使用模擬器運(yùn)行之后按Command+S,即可保持截圖

AppStore介紹截圖制作:

視頻

錄制步驟:

  • 連接設(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)行整理。

上傳

介紹

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 :

中文:

律動(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è)備

反饋:


被拒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

qrcode-share.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容