- 投稿日:2022-03-22T20:07:41+09:00
Top 5 Trusted iOS App Development Companies in the USA
Here is the list of the best iOS app development companies that provide the best solution in the USA AppsChopper: AppsChopper is a reputable iOS app development company with offices in New York and Massachusetts. App developers build interactive and high-performing native applications for iPhone, iPad, and other iOS related devices. Our team has successfully delivered multiple iOS projects with excellent customer satisfaction. Developers working at AppsChopper have hands-on experience in different programming languages, like Swift and Objective-C. They even excel coding on the iOS software development kit (SDK) and frameworks, like CoreData. As a leading iOS app development company, we know how to meet our client's expectations and generate higher revenues. We have worked with top and creative companies and built high-quality iOS applications. Space-o Technologies: Space-o is a USA-based iOS app development company that has delivered top-notch iOS services to its clients. Developers working at our organization have designed and developed 2800+ iOS mobile applications. We cater to the needs of small startups and well-established enterprises of different industry niches. Apps developed by our iOS app developers are successfully running in the Apple Store, and nearly 30 million users are accessing the app. App developers of Space-o technologies are well-versed with the latest technologies, like Blockchain, Machine Learning, Augmented or Virtual Reality, and Artificial Intelligence. We guide you from app ideation to design, development, and mobile application launch on the App Store. WillowTree, Inc.: WillowTree is a well-known iOS app development company founded in 2007. WillowTree has been actively building high-performing iOS applications ever since the iOS SDK was released. There are 200+ team members in WillowTree, who have launched many iOS applications. This organization is considered one of the leading iOS app development companies because developers of this firm develop quality software in order to ensure that the app is running smoothly. WillowTree has earned a great number of clients who engage with them constantly. Moreover, this organization is recognized with top iOS apps by the Apple App Store and has won many awards. Blue Label Labs: Blue Label Labs is one of the top-rated iOS app development companies. For the past 10 years, this company has designed and built many custom iOS applications for startups and well-established enterprises. Blue Label Labs has been recognized as one of the top leading iPhone app development companies for delivering engaging and aesthetically-pleasing applications. Our developers follow a strategic iOS app development process to give an excellent shot of success. Our client's data remains confidential as we sign a non-disclosure agreement. We also build apps that function as a third-party integration for various applications, like healthcare, POS systems, etc. ZCO Corporation: ZCO Corporation is another leading iPhone app development company. We are familiar with cutting-edge technology, and our developers are unmatched in developing high-end, scalable, and secure applications. Our developers have served clients and helped them become market leaders by building custom iOS applications. Developers working at ZCO Corporation have in-depth knowledge of Swift, Objective-C, Xamarin, and React Native. With 30 years of experience, we have guided our clients in empowering their sales team or providing real-time information for the client's IT department. iOS app developers follow a complete app development method that includes app consultation, wireframing to coding and testing to post-app launch maintenance & support.
- 投稿日:2022-03-22T18:00:30+09:00
【Swift】テキストフィールド数値のみの入力制限
iOSでよく扱う入力制限についての備忘録です @IBOutlet private weak var quantityTextField: UITextField! override func viewDidLoad() { super.viewDidLoad() quantityTextField.delegate = self } //省略 extension ViewController: UITextFieldDelegate { func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool { guard let text = textField.text else { return false } if textField === quantityTextField { //数値のみチェック guard string.isEmpty || !string.match(#"^\d+$"#).isEmpty else { return false } //レンジを取得 guard let textRange = Range(range, in: text) else { return false } // `textRange`の範囲をstringへ置換 let updatedText = text.replacingCharacters(in: textRange, with: string) //桁数チェック guard updatedText.count < 6 else { return false } //最初に0は拒否 guard updatedText.match(#"^0"#).isEmpty else { return false } } return true } } 正規表現を扱うのでStringクラスを拡張 extension String { func match(_ pattern: String) -> [String] { guard let regex = try? NSRegularExpression(pattern: pattern), let matched = regex.firstMatch(in: self, range: NSRange(location: 0, length: self.count)) else { return [] } return (0 ..< matched.numberOfRanges).map { NSString(string: self).substring(with: matched.range(at: $0)) } } } おかしいところがもしあれば教えてください?♂️
- 投稿日:2022-03-22T09:39:46+09:00
FlutterのCupertinoTabBarの高さやTextStyleを自由に変更したい
はじめに アプリの画面切り替えに使うTabBarの選択肢として、CupertinoTabBarは便利ですが、 CupertinoTabBarにもう少し設定を加えたい!って思っても、 引数に自分が調整したいプロパティがなくて、困った!ってことはないでしょうか。 そんなときの解決策の案の一つとして、今回の記事を紹介したいと思います。 通常のCupertinoTabBarを使用した場合 main.dart import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { // タブに連動して表示させたい画面のリスト final List<Widget> _pageWidgets = [ Center(child: Container(child: Text('ホーム'))), Center(child: Container(child: Text('ユーザー'))), Center(child: Container(child: Text('お気に入り'))), Center(child: Container(child: Text('設定'))), ]; // タブに実際に表示させたいアイコン等のリスト final List<BottomNavigationBarItem> _tabItems = [ BottomNavigationBarItem( icon: Icon(Icons.home), label: 'ホーム', ), BottomNavigationBarItem( icon: Icon(Icons.person), label: 'ユーザー', ), BottomNavigationBarItem( icon: Icon(Icons.favorite), label: 'お気に入り', ), BottomNavigationBarItem( icon: Icon(Icons.settings), label: '設定', ), ]; @override Widget build(BuildContext context) { return CupertinoTabScaffold( // 通常のCupertinoTabBarを使用 tabBar: CupertinoTabBar( items: _tabItems, activeColor: Colors.yellowAccent, inactiveColor: Colors.white, backgroundColor: Colors.blueGrey, ), tabBuilder: (context, index) { return CupertinoTabView(builder: (context) { return _pageWidgets[index]; }); }, ); } } 上記のコードを例として、本記事を進めていきます。 こちらの場合は、通常のCupertinoTabBarが使用されており、 見た目に関して、下記のように表示されます。 通常のCupertinoTabBarの状態では、アイコンと上のタブバーの余白の間隔がかなり狭い印象を受けますね。 CupertinoTabBarは、余白の調整やタブバーの文字に対してTextStyleの設定ができなかったり、 細かく設定したい時に困ります。 なので、どうすべきかというと・・・・ 解決策 結論、CupertinoTabBarを継承したクラスを作ることです。 例えば、余白を無理矢理作るために、BottomNavigationBarItemに余白分のSizedBoxを追加しても、 サイズエラー等に引っかかりうまくいきません。 また今後別の箇所も変更したい場合を想定すると、クラスを作ったほうがいいと思います。 今回はCupertinoTabBarに高さ(height)と、 タブバーの文字が選択されている状態と通常状態(activeLabelStyleとlabelStyle)を変更できるように、 プロパティを追加した自作クラスを紹介していきたいと思います。 それを実現するようにCupertinoTabBarを継承して作成したCustomCupertinoTabBarは以下の通りになります。 custom_cupertino_tab_bar.dart import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter/cupertino.dart'; // ナビゲーションバーの基本の高さ const double _defaultTabBarHeight = 50.0; const Color _defaultTabBarBorderColor = CupertinoDynamicColor.withBrightness( color: Color(0x4C000000), darkColor: Color(0x29000000), ); const Color _defaultTabBarInactiveColor = CupertinoColors.inactiveGray; class CustomCupertinoTabBar extends CupertinoTabBar { CustomCupertinoTabBar({ Key? key, required this.items, this.onTap, this.currentIndex = 0, this.backgroundColor, this.activeColor, this.inactiveColor = _defaultTabBarInactiveColor, this.iconSize = 30.0, this.border = const Border( top: BorderSide( color: _defaultTabBarBorderColor, width: 0.0, style: BorderStyle.solid, ), ), this.height, this.labelStyle, this.activeLabelStyle, }) : super(items: items); final List<BottomNavigationBarItem> items; final ValueChanged<int>? onTap; final int currentIndex; final Color? backgroundColor; final Color? activeColor; final Color inactiveColor; final double iconSize; final Border? border; // タブバーの高さの設定 final double? height; // タブバーのデフォルトのテキストスタイル final TextStyle? labelStyle; // タブバーが選択されている状態のテキストスタイル final TextStyle? activeLabelStyle; @override Size get preferredSize => Size.fromHeight(height != null ? height! : _defaultTabBarHeight); bool opaque(BuildContext context) { final Color backgroundColor = this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor; return CupertinoDynamicColor.resolve(backgroundColor, context).alpha == 0xFF; } @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final double bottomPadding = MediaQuery.of(context).padding.bottom; final Color backgroundColor = CupertinoDynamicColor.resolve( this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor, context, ); BorderSide resolveBorderSide(BorderSide side) { return side == BorderSide.none ? side : side.copyWith( color: CupertinoDynamicColor.resolve(side.color, context)); } final Border? resolvedBorder = border == null || border.runtimeType != Border ? border : Border( top: resolveBorderSide(border!.top), left: resolveBorderSide(border!.left), bottom: resolveBorderSide(border!.bottom), right: resolveBorderSide(border!.right), ); final Color inactive = CupertinoDynamicColor.resolve(inactiveColor, context); final double setHeight = height != null ? height! : _defaultTabBarHeight; Widget result = DecoratedBox( decoration: BoxDecoration( border: resolvedBorder, color: backgroundColor, ), child: SizedBox( // タブバーの高さが設定される。 height: setHeight + bottomPadding, child: IconTheme.merge( data: IconThemeData(color: inactive, size: iconSize), child: DefaultTextStyle( style: CupertinoTheme.of(context) .textTheme .tabLabelTextStyle .copyWith(color: inactive), child: Padding( padding: EdgeInsets.only(bottom: bottomPadding), child: Semantics( explicitChildNodes: true, child: Row( crossAxisAlignment: CrossAxisAlignment.end, children: _buildTabItems(context), ), ), ), ), ), ), ); if (!opaque(context)) { result = ClipRect( child: BackdropFilter( filter: ImageFilter.blur(sigmaX: 10.0, sigmaY: 10.0), child: result, ), ); } return result; } List<Widget> _buildTabItems(BuildContext context) { final List<Widget> result = <Widget>[]; final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); for (int index = 0; index < items.length; index += 1) { final bool active = index == currentIndex; result.add( _wrapActiveItem( context, Expanded( child: Semantics( selected: active, hint: localizations.tabSemanticsLabel( tabIndex: index + 1, tabCount: items.length, ), child: GestureDetector( behavior: HitTestBehavior.opaque, onTap: onTap == null ? null : () { onTap!(index); }, child: Padding( padding: const EdgeInsets.only(bottom: 4.0), child: Column( mainAxisAlignment: MainAxisAlignment.end, children: _buildSingleTabItem(items[index], active), ), ), ), ), ), active: active, ), ); } return result; } List<Widget> _buildSingleTabItem(BottomNavigationBarItem item, bool active) { return <Widget>[ Expanded( child: Center(child: active ? item.activeIcon : item.icon), ), if (item.title != null) item.title!, if (item.label != null) Text(item.label!), ]; } Widget _wrapActiveItem(BuildContext context, Widget item, {required bool active}) { if (!active) { // 設定されたlabelStyleの適用 if (labelStyle != null) { return DefaultTextStyle.merge( style: labelStyle, child: item, ); } else { return item; } } final Color activeColor = CupertinoDynamicColor.resolve( this.activeColor ?? CupertinoTheme.of(context).primaryColor, context, ); return IconTheme.merge( data: IconThemeData(color: activeColor), child: DefaultTextStyle.merge( // 設定されたactiveLabelStyleが適用される style: activeLabelStyle != null ? activeLabelStyle : TextStyle(color: activeColor), child: item, ), ); } // 今後プロパティを追加する場合、copyWithにも記述する // 記述しないとUIに反映されない @override CustomCupertinoTabBar copyWith({ Key? key, List<BottomNavigationBarItem>? items, Color? backgroundColor, Color? activeColor, Color? inactiveColor, double? iconSize, Border? border, int? currentIndex, ValueChanged<int>? onTap, double? height, TextStyle? labelStyle, TextStyle? activeLabelStyle, }) { return CustomCupertinoTabBar( key: key ?? this.key, items: items ?? this.items, backgroundColor: backgroundColor ?? this.backgroundColor, activeColor: activeColor ?? this.activeColor, inactiveColor: inactiveColor ?? this.inactiveColor, iconSize: iconSize ?? this.iconSize, border: border ?? this.border, currentIndex: currentIndex ?? this.currentIndex, onTap: onTap ?? this.onTap, height: height ?? this.height, labelStyle: labelStyle ?? this.labelStyle, activeLabelStyle: activeLabelStyle ?? this.activeLabelStyle, ); } } 今回のポイントとして、元々のWidgetの表示をあまり変えたくなかったので、 ほぼCupertinoTabBarのソースコードをそのままコピーしています。 また、UIの部分を変化させたいときのポイントについて、 上書きするbuildメソッドに、具体的に表示させるものを記述します。 そしてcopyWithメソッドも記述しないと反映されないので注意。 元々の記述にはなくて、今回のために変更した箇所をフォーカスして説明していきます。 // ... 略 class CustomCupertinoTabBar extends CupertinoTabBar { CustomCupertinoTabBar({ // ... 略 this.height, this.labelStyle, this.activeLabelStyle, }) : super(items: items); // ... 略 // タブバーの高さの設定 final double? height; // タブバーのデフォルトのテキストスタイル final TextStyle? labelStyle; // タブバーが選択されている状態のテキストスタイル final TextStyle? activeLabelStyle; // ... 略 } 今回、通常のCupertinoTabBarにはなかった height, labelStyle, activeLabelStyleプロパティを作成して、 constructorの引数としても受け取れるようにしました。 そして、受け取ったheight等はどう適用されていくかというと・・・ // ナビゲーションバーの基本の高さ const double _defaultTabBarHeight = 50.0; // ... 略 class CustomCupertinoTabBar extends CupertinoTabBar { // ... 略 final double setHeight = height != null ? height! : _defaultTabBarHeight; // ... 略 @override Widget build(BuildContext context) { // ... 略 Widget result = DecoratedBox( // ... 略 child: SizedBox( // タブバーの高さが設定される。 height: setHeight + bottomPadding, // ... 略 ), ); // ... 略 return result; } // ... 略 } まず、heightについて、 上記のように、buildメソッドで使われるSizedBoxの高さとして、設定されます。 そして、labelStyleとactiveLabelStyleを見ていくと、 // ... 略 class CustomCupertinoTabBar extends CupertinoTabBar { // ... 略 Widget _wrapActiveItem(BuildContext context, Widget item, {required bool active}) { if (!active) { // 設定されたlabelStyleの適用 if (labelStyle != null) { return DefaultTextStyle.merge( style: labelStyle, child: item, ); } else { return item; } } final Color activeColor = CupertinoDynamicColor.resolve( this.activeColor ?? CupertinoTheme.of(context).primaryColor, context, ); return IconTheme.merge( data: IconThemeData(color: activeColor), child: DefaultTextStyle.merge( // 設定されたactiveLabelStyleが適用される style: activeLabelStyle != null ? activeLabelStyle : TextStyle(color: activeColor), child: item, ), ); } // ... 略 } labelStyleとactiveLabelStyleについて、 DefaultTextStyleのmergeメソッド使って、引数のWidgetのテキストスタイルを変更するようにしています。 // ... 略 class CustomCupertinoTabBar extends CupertinoTabBar { // ... 略 @override CustomCupertinoTabBar copyWith({ // ... 略 double? height, TextStyle? labelStyle, TextStyle? activeLabelStyle, }) { return CustomCupertinoTabBar( // ... 略 height: height ?? this.height, labelStyle: labelStyle ?? this.labelStyle, activeLabelStyle: activeLabelStyle ?? this.activeLabelStyle, ); } } 最後にcopyWithメソッドにも記述しないと反映されないので、注意してください。 では、今回作成したクラスを使ってみましょう。 main.dartを変更しましょう。 main.dart import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; // インポートする import 'custom_cupertino_tabbar.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatelessWidget { // タブに連動して表示させたい画面のリスト final List<Widget> _pageWidgets = [ Center(child: Container(child: Text("ホーム"))), Center(child: Container(child: Text("ユーザー"))), Center(child: Container(child: Text("お気に入り"))), Center(child: Container(child: Text("設定"))), ]; // タブに実際に表示させたいアイコン等のリスト final List<BottomNavigationBarItem> _tabItems = [ BottomNavigationBarItem( icon: Icon(Icons.home), label: 'ホーム', ), BottomNavigationBarItem( icon: Icon(Icons.person), label: 'ユーザー', ), BottomNavigationBarItem( icon: Icon(Icons.favorite), label: 'お気に入り', ), BottomNavigationBarItem( icon: Icon(Icons.settings), label: '設定', ), ]; @override Widget build(BuildContext context) { return CupertinoTabScaffold( // 作成したクラスに差し替える tabBar: CustomCupertinoTabBar( items: _tabItems, activeColor: Colors.yellowAccent, inactiveColor: Colors.white, backgroundColor: Colors.blueGrey, // 追加したプロパティを記述する height: 80, labelStyle: TextStyle( color: Colors.white, fontWeight: FontWeight.normal, fontSize: 12, ), activeLabelStyle: TextStyle( color: Colors.yellowAccent, fontWeight: FontWeight.bold, fontSize: 14, ), ), tabBuilder: (context, index) { return CupertinoTabView(builder: (context) { return _pageWidgets[index]; }); }, ); } } これで、CupertinoTabBarの高さやTextStyleが設定できるようになりました。 他の項目も調整していきたいっていうことであれば、 プロパティの追加とbuildメソッドとcopyWithメソッドに手を加えていく感じになります。 おわり 以上、『CupertinoTabBarの高さやTextStyleを自由に変更したい』についての記事でした。 本記事が誰かの助けになれば嬉しいです。 最後までご覧いただきありがとうございました。 参考資料