您的位置:

iOS Storyboard快速提升APP用户体验

iOS Storyboard是苹果公司在Xcode 4.2及以后版本提出的新概念。它是一个可视化的界面设计工具,可以不用编写和管理多个.xib文件,使得构建iOS应用变得更加方便和高效。

一、Storyboards的优点

1、时间优势:

相对于XIB和纯代码开发,Storyboard更方便快捷,更直观、易于管理和分享。 Storyboard将App 的始终状态和过渡状态全部可视化,随时预览各个页面之间的关系和交互。

2、构建方便:

Storyboard提供了交互式的界面构建方式。传统的每个界面都要一个xib和一个独立的控制器,而使用Storyboard, 每个界面都在一个文件里,而且可以通过segue 连接在一起,快捷地跳转页面,代码轻量化,减少了开发难度。

3、设计协作更易:

Storyboards动画过程和界面的设计、视觉效果很好的融合在一起,对于UI和UX设计师来说非常容易分享与协作。多位开发者与设计者可以协同工作,并搭建各自的界面,最终进行集成管理时间成本和开发成本。

二、如何使用Storyboard编写UI

1、创建Storyboard文件

在Xcode中创建一个新的项目并选中Single View Application。在这个模板中,可以创建一个ViewController界面,Storyboard, Storyboard 2.0或Empty模板,点击Create, 生成一个新项目。

2、添加场景(View Controllers)

可以通过拖拽、条件转移等方式,完成视图控制器的添加。可以使用drag手势轻松地添加View Controller到graph canvas区域。

<!--Storyboard示例代码-->
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB"
 version="3.0" 
 toolsVersion="14113.1" 
 systemVersion="14A238x" 
 targetRuntime="iOS.CocoaTouch" 

...
<initialViewController
sceneMemberID="viewController">
     <viewController
     id="vS7-qU-Bmw"
     customClass="ViewController"
     customModule="StoryboardApp"
     sceneMemberID="viewController">
    ...
</initialViewController>
    ...
</document>

3、设置View Controller

可以设置Scene的常用属性,比如场景的ID和type,标识每个ViewController,设置自定义Class,下一个VC的各种传递参数、数据及数据绑定等。

<!--示例代码-->
 <scene sceneID="...">
      <objects>
         <viewController
...
</scene>

4、设置标识符和segue

设置Storyboard标识符的目的是便于其他ViewController可以在程序中随时访问。Segue表示不同屏幕间的转换关系,可以赋予动画效果,通过present或dismiss方式,决定页面如何跳转,实现应用流转。

<!--Storyboard示例代码-->
 <segue kind="show" 
        destination="NavigationControllerID" 
        identifier="segueID"/>

三、Storyboard和Auto Layout的深度整合

1、Storyboard+AutoLayout

Storyboard和Auto Layout是紧密无缝集成的,在Storyboard上添加和编辑约束和布局规则,即可实现视图比例的变化和分配,以满足任何屏幕大小下的需要。

2、Auto-Layout与Storyboard类

Storyboard和Auto Layout完美地集成在一起,类似机柜的操作。不需要手动输入自带代码,调整约束就可以在Storyboard上看到界面效果。如果还没有跑在设备上,就可以看到勾画出的视图框架,快捷直观,降低了开发难度。

3、更新约束

约束是在Auto Layout的约束编辑器中编辑的,为了获取更好的效果,可以对其进行更新或者重新调整。这可以通过改变Storyboard中界面的大小来达到,如果界面的长宽比例变了,那么它的相对约束会根据你所选择的约束自动更新。

四、Storyboards布局动画

1、属性动画

在Storyboard中选择视图控件后,通过属性窗配置动画的起始和结束值,选择动画的插值函数等,就可以用属性动画在Storyboard中轻易地创建简单的动画效果。

<!--Storyboard示例代码-->
<userDefinedRuntimeAttributes>
        <userDefinedRuntimeAttribute type="number" 
        value="50" 
        keyPath="transform.translation.x"/>
        <userDefinedRuntimeAttribute type="number" 
        value="50" 
        keyPath="transform.translation.y"/>
</userDefinedRuntimeAttributes>

2、可交互式动画

对于复杂的交互式动画,可以使用Core Animation的相关层。比如,动画颜色的变化可以用CAGradientLayer来实现;有关图层图形变换的动画可以用CALayer来实现。

3、代码控制动画

编写代码控制动画比较繁琐,不过在某些特殊情况下还是必须用到的。比如在动画复杂的交互中,需要使用UIView中的block进行UIView动画管理,简化代码复杂度。

<!--代码示例-->
UIView.animate(withDuration: 0.15, animations: { 
    self.view.center = CGPoint(x: self.view.center.x, 
    y: self.view.center.y + delta) 
}, completion: { (complete: Bool) in 
    UIView.animate(withDuration: 0.15, animations: { 
        self.view.transform = CGAffineTransform.identity }) }) 

五、Storyboards和CocoaPods集成

1、CocoaPods

CocoaPods 是一种供 iOS 开发人员使用的库管理器,可以找到并下载第三方库,从而更高效地进行开发。使用CocoaPods, iOS 开发人员可以在项目中通过配置文件一步安装第三方库,不需要人工查找、下载和安装,可快速方便地实现代码复用。

2、Storyboards和CocoaPods的集成

集成CocoaPods和Storyboards可以简化开发过程,节省时间。先通过Terminal窗口cd到你的项目所在根目录,在终端中运行pod init命令,创建Podfile,Open Podfile,在Podfile中添加你需要的Pod。然后, 打开终端,运行pod install命令,等待几分钟。

<!--Podfile示例代码-->
use_frameworks!

target 'MyApp' do
   pod 'SwiftyJSON', '~> 3.1.1' 
   pod 'Alamofire', '~> 4.7'
end

#post install actions
post_install do |installer|
    installer.pods_project.build_configurations.each do |config|
        config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '9.0'
    end
end

六、Storyboard中的数据存储和购买

1、数据存储

使用Storyboards可以方便地实现数据存储。可以使用SQLite、CoreData和NSUserDefaults在Storyboards中存储数据。使用NSUserDefaults存储数据可快速、轻松地在简单的App 中存储数据。MongoDB等NoSQL数据库存储数据则在简单的文档中进行,也是一种可选方案。

2、购买

内购是App在商业层面实现盈利的方式之一。IAP(In-App Purchase) 是iOS开发者平台提供的API集,可以让开发者的应用内完成对沙盒测试环境的模拟和交互式购买。IAP API 和 Storyboard 结合,可以在应用的购买流程上迅速构建高效的IAP System。在SDK中内嵌代码,你可以快速找到并使用这些组件。

七、优化Storyboard的性能

1、减少Storyboards的复杂度

减少Storyboards的页面和Controller的数量,通过Segue缩减页面间的跳转,也可以提高Storyboards的性能。过于庞大的Storyboards会拖累系统性能,导致应用运行缓慢。

2、通过使用局部载入Storyboards(Storyboard References)来提高性能

划分Storyboards,使用Storyboard References可以缩短Storyboards载入的时间,优化Storyboards加载和应用启动的时间。

3、Storyboard中使用Xib

Storyboard对于大型应用程序可能会变得难以维护,对于这种情况,可以使用XIB文件用代码某个精简功能来避免复杂的Storyboard。再通过某种按钮使该页面进入Storyboard中。

结论

总体来说,使用Storyboard可避免error-prone的手工界面构建,可以直接呈现整个项目的预览。它的集成优势、可协同性和实现优化,加快了开发流程,最终减少了项目工作量以及与整合时间和人力成本。即使完美匹配Auto Layout和CocoaPods,Storyboards重点在于提高生产力,可以为设计师、开发人员和UX提供重要的便利。

iOS Storyboard快速提升APP用户体验

2023-05-16
提升App用户体验的方法

在如今竞争激烈的App市场中,一个优秀的用户体验是保持和吸引用户的关键点。为了提高App产品的用户体验,以下我们从多个方面探讨如何优化App,以增加用户的留存率和忠诚度。 一、呈现清晰的内容 为了提高

2023-12-08
印象笔记记录java学习(Java成长笔记)

2022-11-12
Android App开发:如何提升用户体验

在如今的移动互联网时代,用户体验是一个成功的Android应用程序的重要因素。无论应用程序功能强大还是漂亮的UI设计,如果用户体验不好,用户会很快放弃使用。因此,在我们进行Android App开发的

2023-12-08
StoryBoard的使用与优势分析

2023-05-19
iOS 画中画:提升用户体验的利器

2023-05-21
提升Android应用用户体验的秘诀

对于现代人来说,生活离不开手机和App。随着移动互联网的快速发展,移动应用市场日益庞大,应用数量多样性极高,用户对应用的体验要求也越来越高。一个具有良好用户体验的应用,可以帮助用户更加便利的完成各种任

2023-12-08
如何使用iOS本地通知提升用户体验

2023-05-16
ios原生调用jsapi,ios原生app

本文目录一览: 1、如何用js调用ios 2、iOS - OC 与 JS 交互六种方式总结 3、原生APP中js怎样与Android和ios进行交互 4、如何用JS调用几十万原生API 如何用js调用

2023-12-08
提高APP用户体验的必备工具

移动应用程序(APP)是数字时代的重点创新之一,随着基础设施的建设和硬件的成本下降,越来越多的人使用智能手机,推动了移动应用市场的迅速增长。然而,APP市场的激烈竞争和用户采用不悔改变的态度,使得提供

2023-12-08
提高用户体验的Android搜索框设计技巧

2023-05-14
优雅地使用Flutter在Android开发中提升用户体验

2023-05-14
提升用户体验的Python标签设计

2023-05-12
iOS Deep Link详解

2023-05-19
提升Android TV用户交互体验的最佳实践

2023-05-14
提升用户体验的Android主题设计

2023-05-14
使用FastAPIRequest提升网站流量与用户体验

2023-05-19
提升用户体验的欢迎界面设计

欢迎界面是用户接触产品的第一印象,在产品使用过程中也是频繁使用的场景之一。因此,好的欢迎界面设计能够提升用户的使用体验,降低用户的学习成本,并且增加产品的美观性。在这篇文章中,我们将从多个方面阐述如何

2023-12-08
when-present<#else>when-missing. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: ${item.id} [in template "article/detail/index.ftl" at line 48, column 106] ---- Java stack trace (for programmers): ---- freemarker.core.InvalidReferenceException: [... Exception message was already printed; see it above ...] at freemarker.core.InvalidReferenceException.getInstance(InvalidReferenceException.java:134) at freemarker.core.EvalUtil.coerceModelToTextualCommon(EvalUtil.java:481) at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:401) at freemarker.core.EvalUtil.coerceModelToStringOrMarkup(EvalUtil.java:370) at freemarker.core.DollarVariable.calculateInterpolatedStringOrMarkup(DollarVariable.java:104) at freemarker.core.DollarVariable.accept(DollarVariable.java:63) at freemarker.core.Environment.visit(Environment.java:371) at freemarker.core.IteratorBlock$IterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:321) at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:271) at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:244) at freemarker.core.Environment.visitIteratorBlock(Environment.java:645) at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:108) at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94) at freemarker.core.Environment.visit(Environment.java:335) at freemarker.core.Environment.visit(Environment.java:341) at freemarker.core.Environment.visit(Environment.java:341) at freemarker.core.Environment.process(Environment.java:314) at freemarker.template.Template.process(Template.java:383) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:332) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:266) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:220) at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:181) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:314) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1431) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1167) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1106) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:979) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1014) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:903) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:564) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:885) at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:658) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:205) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at com.software.filter.HttpSpiderIdentifyFilter.doFilter(HttpSpiderIdentifyFilter.java:51) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:116) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:174) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:149) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:167) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:90) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:482) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:115) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:93) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:74) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:340) at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:391) at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:63) at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:896) at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1744) at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:52) at java.base/java.lang.VirtualThread.run(VirtualThread.java:309)