tgios 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +23 -0
- data/Gemfile +11 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +23 -0
- data/README.md +0 -0
- data/Rakefile +20 -0
- data/app/app_delegate.rb +14 -0
- data/lib/tgios/animatable_view.rb +50 -0
- data/lib/tgios/binding_base.rb +38 -0
- data/lib/tgios/common_ui_utility.rb +30 -0
- data/lib/tgios/custom_method.rb +35 -0
- data/lib/tgios/extended_ui_table_view.rb +37 -0
- data/lib/tgios/extended_ui_view_controller.rb +42 -0
- data/lib/tgios/image_loader.rb +60 -0
- data/lib/tgios/loading_indicator.rb +47 -0
- data/lib/tgios/model_errors_helper.rb +20 -0
- data/lib/tgios/ns_date_helper.rb +25 -0
- data/lib/tgios/photo_controller.rb +35 -0
- data/lib/tgios/photo_scroll_view.rb +61 -0
- data/lib/tgios/search_controller.rb +77 -0
- data/lib/tgios/ui_button_binding.rb +36 -0
- data/lib/tgios/ui_picker_view_list_binding.rb +53 -0
- data/lib/tgios/ui_table_view_list_binding.rb +70 -0
- data/lib/tgios/ui_table_view_model_binding.rb +373 -0
- data/lib/tgios/ui_table_view_model_list_binding.rb +173 -0
- data/lib/tgios/ui_text_field_binding.rb +268 -0
- data/lib/tgios/ui_text_view_binding.rb +177 -0
- data/lib/tgios/version.rb +3 -0
- data/lib/tgios.rb +17 -0
- data/resources/Default-568h@2x.png +0 -0
- data/spec/main_spec.rb +9 -0
- data/tgios.gemspec +27 -0
- metadata +137 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
YjFlOTkxYjQ2MGRmMDAwNDE5MTZhYzBhZWI3MzQ5Y2U4NTcwOTcyYQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NGNkYThiNTU4YWUxMjc2OTQxOTkxYWFkMjFiMWIzYjkyNDQxZThhNQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MzBkZDQzYzllMjNiNGI1MzNkNTk3ZWM5ZWUyNzU4NDk1YTM4NDFlNWFiNWZh
|
10
|
+
ZmJhZGI4MGFiZDc1YWExZDcxNmQwNmRhYWQxYjE0ZjJiZjMwNzNkMzBjOGEw
|
11
|
+
MDU5MzUyZWMyZTQ5MDYxNjIxZmU1ZWIyNjg1Nzc5NmNiMGQyMzQ=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NDlkNDkwMGZmY2U5YWNjYmNkNzU1ZDY5ZGJiNjk3MDgxMTQyYWVmYTNlZTEw
|
14
|
+
OTliODJhMjk5NGYwNjk0YzBjMTMyODdlODZjOGM3MTAxYmU2YjBkNDc1NzNh
|
15
|
+
MjRjMzJjYTYzOWU3MGY0MzFlNDczNTEzMDljMWY0OGU1NWRlYzI=
|
data/.gitignore
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
*.gem
|
2
|
+
|
3
|
+
.repl_history
|
4
|
+
build
|
5
|
+
tags
|
6
|
+
app/pixate_code.rb
|
7
|
+
resources/*.nib
|
8
|
+
resources/*.momd
|
9
|
+
resources/*.storyboardc
|
10
|
+
.DS_Store
|
11
|
+
nbproject
|
12
|
+
.redcar
|
13
|
+
#*#
|
14
|
+
*~
|
15
|
+
*.sw[po]
|
16
|
+
.eprj
|
17
|
+
.sass-cache
|
18
|
+
.idea
|
19
|
+
.build
|
20
|
+
build-*
|
21
|
+
.dat*
|
22
|
+
.ruby-gemset
|
23
|
+
.ruby-version
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
tgios (0.0.1)
|
5
|
+
awesome_print_motion
|
6
|
+
motion-layout
|
7
|
+
plastic_cup (>= 0.1.1)
|
8
|
+
sugarcube (= 1.1.0)
|
9
|
+
|
10
|
+
GEM
|
11
|
+
remote: https://rubygems.org/
|
12
|
+
specs:
|
13
|
+
awesome_print_motion (0.1.0)
|
14
|
+
motion-layout (0.0.1)
|
15
|
+
motion-stump (0.3.0)
|
16
|
+
plastic_cup (0.1.1)
|
17
|
+
rake (10.1.0)
|
18
|
+
sugarcube (1.1.0)
|
19
|
+
|
20
|
+
PLATFORMS
|
21
|
+
ruby
|
22
|
+
|
23
|
+
DEPENDENCIES
|
24
|
+
motion-stump
|
25
|
+
rake
|
26
|
+
tgios!
|
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (c) 2014, April
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without modification,
|
5
|
+
are permitted provided that the following conditions are met:
|
6
|
+
|
7
|
+
Redistributions of source code must retain the above copyright notice, this
|
8
|
+
list of conditions and the following disclaimer.
|
9
|
+
|
10
|
+
Redistributions in binary form must reproduce the above copyright notice, this
|
11
|
+
list of conditions and the following disclaimer in the documentation and/or
|
12
|
+
other materials provided with the distribution.
|
13
|
+
|
14
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
15
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
16
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
17
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
18
|
+
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
19
|
+
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
20
|
+
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
21
|
+
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
22
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
23
|
+
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
$:.unshift("/Library/RubyMotion/lib")
|
3
|
+
require 'motion/project/template/ios'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bundler'
|
7
|
+
Bundler.require
|
8
|
+
require 'motion-stump'
|
9
|
+
require 'sugarcube'
|
10
|
+
require 'plastic_cup'
|
11
|
+
require 'plastic_cup/stylesheet'
|
12
|
+
rescue LoadError
|
13
|
+
end
|
14
|
+
|
15
|
+
Motion::Project::App.setup do |app|
|
16
|
+
# Use `rake config' to see complete project settings.
|
17
|
+
app.name = 'tgios'
|
18
|
+
app.identifier = 'com.tofugear.tgios'
|
19
|
+
app.specs_dir = "spec/"
|
20
|
+
end
|
data/app/app_delegate.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
3
|
+
return true if RUBYMOTION_ENV == 'test'
|
4
|
+
|
5
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
6
|
+
ctlr = MyController.new
|
7
|
+
@window.rootViewController = ctlr
|
8
|
+
@window.makeKeyAndVisible
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
class MyController < UIViewController
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Tgios
|
2
|
+
class AnimatableView < UIView
|
3
|
+
DURATION = 0.25
|
4
|
+
|
5
|
+
def initWithFrame(frame)
|
6
|
+
super
|
7
|
+
bg_view = PlasticCup::Base.style(UIView.new,
|
8
|
+
frame: self.bounds,
|
9
|
+
backgroundColor: :white.uicolor,
|
10
|
+
alpha: 0.95)
|
11
|
+
self.addSubview(bg_view)
|
12
|
+
self
|
13
|
+
end
|
14
|
+
|
15
|
+
def show_animated
|
16
|
+
@show_notification ||= NSNotification.notificationWithName(
|
17
|
+
UIKeyboardWillShowNotification,
|
18
|
+
object: self,
|
19
|
+
userInfo:{UIKeyboardFrameEndUserInfoKey=> NSValue.valueWithCGRect(self.frame),
|
20
|
+
UIKeyboardAnimationCurveUserInfoKey=> UIViewAnimationOptionCurveEaseInOut,
|
21
|
+
UIKeyboardAnimationDurationUserInfoKey=> DURATION})
|
22
|
+
UIView.animateWithDuration(DURATION, animations: ->{
|
23
|
+
NSNotificationCenter.defaultCenter.postNotification(@show_notification)
|
24
|
+
self.alpha = 1.0
|
25
|
+
frame = self.frame
|
26
|
+
frame.origin.y = self.superview.frame.size.height - self.bounds.size.height
|
27
|
+
self.frame = frame
|
28
|
+
})
|
29
|
+
@is_shown = true
|
30
|
+
end
|
31
|
+
|
32
|
+
def hide_animated
|
33
|
+
if @is_shown
|
34
|
+
@hide_notification ||= NSNotification.notificationWithName(
|
35
|
+
UIKeyboardWillHideNotification,
|
36
|
+
object: self,
|
37
|
+
userInfo:{UIKeyboardAnimationCurveUserInfoKey=> UIViewAnimationOptionCurveEaseInOut,
|
38
|
+
UIKeyboardAnimationDurationUserInfoKey=> DURATION})
|
39
|
+
UIView.animateWithDuration(DURATION, animations: ->{
|
40
|
+
NSNotificationCenter.defaultCenter.postNotification(@hide_notification)
|
41
|
+
self.alpha = 0.0
|
42
|
+
frame = self.frame
|
43
|
+
frame.origin.y = self.superview.frame.size.height
|
44
|
+
self.frame = frame
|
45
|
+
})
|
46
|
+
@is_shown = false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Tgios
|
2
|
+
class BindingBase
|
3
|
+
def initialize(*arg)
|
4
|
+
@hook_bindings = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def prepareForRelease
|
8
|
+
@hook_bindings = nil
|
9
|
+
onPrepareForRelease
|
10
|
+
end
|
11
|
+
|
12
|
+
def onPrepareForRelease
|
13
|
+
raise NotImplementedError.new("prepareForRelease not overridden for class #{self.class.name}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def hook(control, event, &block)
|
17
|
+
binding=if control.is_a?(UIButton)
|
18
|
+
UIButtonBinding.new.bind(control).on(event, &block)
|
19
|
+
end
|
20
|
+
|
21
|
+
@hook_bindings << binding
|
22
|
+
binding
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
def unhook(control, event)
|
27
|
+
if control.is_a?(UIButton)
|
28
|
+
UIButtonBinding.unbind(control)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def dealloc
|
33
|
+
ap "#{self.class.name} dealloc"
|
34
|
+
super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
include SugarCube::CoreGraphics
|
3
|
+
module Tgios
|
4
|
+
class CommonUIUtility
|
5
|
+
def self.imageFromColor(color)
|
6
|
+
|
7
|
+
rect=Rect(0,0,1,1)
|
8
|
+
UIGraphicsBeginImageContext(rect.size)
|
9
|
+
context=UIGraphicsGetCurrentContext()
|
10
|
+
uicolor = color.is_a?(UIColor) ? color : color.uicolor
|
11
|
+
CGContextSetFillColorWithColor(context, uicolor.CGColor)
|
12
|
+
CGContextFillRect(context, rect)
|
13
|
+
image=UIGraphicsGetImageFromCurrentImageContext()
|
14
|
+
UIGraphicsEndImageContext()
|
15
|
+
image
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.fix_orientation(image)
|
19
|
+
if image.imageOrientation == UIImageOrientationUp
|
20
|
+
image
|
21
|
+
else
|
22
|
+
UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale)
|
23
|
+
image.drawInRect([[0,0], image.size])
|
24
|
+
normalized_image = UIImage.UIGraphicsGetImageFromCurrentImageContext
|
25
|
+
UIGraphicsEndImageContext()
|
26
|
+
normalized_image
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Tgios
|
2
|
+
module CustomMethod
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
extend ClassMethods
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
def on(event_name, &block)
|
11
|
+
@events[event_name]=block
|
12
|
+
end
|
13
|
+
|
14
|
+
def off(*event_names)
|
15
|
+
event_names.each {|event_name| @events.delete(event_name) }
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def define_custom_method(name=[])
|
20
|
+
name.each do |fld|
|
21
|
+
define_method fld do
|
22
|
+
self.instance_variable_get(:"@#{fld}")
|
23
|
+
end
|
24
|
+
define_method "#{fld}=" do |value|
|
25
|
+
old_value=self.instance_variable_get(:"@#{fld}")
|
26
|
+
self.instance_variable_set(:"@#{fld}", value)
|
27
|
+
@events[:value_changed].call(self, fld, old_value, value) unless @events.nil? || @events[:value_changed].nil?
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Tgios
|
2
|
+
module ExtendedUITableView
|
3
|
+
include PlasticCup
|
4
|
+
|
5
|
+
def add_full_table_view_to(view)
|
6
|
+
table = Base.style(UITableView.grouped, :grouped_table)
|
7
|
+
Motion::Layout.new do |l|
|
8
|
+
l.view view
|
9
|
+
l.subviews 'table' => table
|
10
|
+
l.vertical '|[table]|'
|
11
|
+
l.horizontal '|[table]|'
|
12
|
+
end
|
13
|
+
table
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_title_to_table_header(title, table)
|
17
|
+
frame = table.bounds
|
18
|
+
frame.size.height = 45
|
19
|
+
table_header = UIView.alloc.initWithFrame(frame)
|
20
|
+
table.tableHeaderView = table_header
|
21
|
+
add_title_to_view(title, table_header)
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_title_to_view(title, view)
|
26
|
+
|
27
|
+
Motion::Layout.new do |l|
|
28
|
+
l.view view
|
29
|
+
l.subviews 'label' => Base.style(UILabel.new, text: title,
|
30
|
+
font: UIFont.boldSystemFontOfSize(19),
|
31
|
+
adjustsFontSizeToFitWidth: true)
|
32
|
+
l.vertical '|-10-[label]|'
|
33
|
+
l.horizontal '|-10-[label]-10-|'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Tgios
|
2
|
+
class ExtendedUIViewController < UIViewController
|
3
|
+
def viewDidLoad
|
4
|
+
super
|
5
|
+
self.edgesForExtendedLayout = UIRectEdgeNone if self.respond_to?(:edgesForExtendedLayout)
|
6
|
+
@bindings=[]
|
7
|
+
end
|
8
|
+
|
9
|
+
def prepareForRelease
|
10
|
+
@bindings=nil
|
11
|
+
onPrepareForRelease
|
12
|
+
end
|
13
|
+
|
14
|
+
def onPrepareForRelease
|
15
|
+
raise NotImplementedError.new("onPrepareForRelease not overridden for class #{self.class.name}")
|
16
|
+
end
|
17
|
+
|
18
|
+
def hook(control, event, &block)
|
19
|
+
binding=if control.is_a?(UIButton)
|
20
|
+
UIButtonBinding.new.bind(control).on(event, &block)
|
21
|
+
end
|
22
|
+
|
23
|
+
@bindings << binding
|
24
|
+
binding
|
25
|
+
|
26
|
+
end
|
27
|
+
|
28
|
+
def viewDidDisappear(animated)
|
29
|
+
if self.isMovingFromParentViewController
|
30
|
+
ap "#{self.class.name} view moving away, prepare for release"
|
31
|
+
# cut off all crap to avoid memory leak
|
32
|
+
self.prepareForRelease()
|
33
|
+
end
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
37
|
+
def dealloc
|
38
|
+
ap "#{self.class.name} dealloc"
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Tgios
|
2
|
+
class ImageLoader < BindingBase
|
3
|
+
|
4
|
+
def initialize(url)
|
5
|
+
super
|
6
|
+
@url = url
|
7
|
+
@events = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def on(event, &block)
|
11
|
+
@events[event]=block
|
12
|
+
end
|
13
|
+
|
14
|
+
def load
|
15
|
+
image = UIImage.imageWithContentsOfFile(self.file_path)
|
16
|
+
if image.nil?
|
17
|
+
AFMotion::HTTP.get(@url) do |result|
|
18
|
+
image = UIImage.imageWithData(result.object)
|
19
|
+
@events[:image_loaded].call(image, result.success?) unless @events.nil? || @events[:image_loaded].nil?
|
20
|
+
NSFileManager.defaultManager.createFileAtPath(self.file_path, contents: result.object, attributes:nil)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
@events[:image_loaded].call(image, true) unless @events.nil? || @events[:image_loaded].nil?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.base_path
|
28
|
+
@base_path ||= "#{NSTemporaryDirectory()}web/"
|
29
|
+
end
|
30
|
+
|
31
|
+
def filename
|
32
|
+
@filename ||= (
|
33
|
+
nsurl = NSURL.URLWithString(@url)
|
34
|
+
path = "#{nsurl.host}#{nsurl.path}"
|
35
|
+
CGI.escape(path)
|
36
|
+
)
|
37
|
+
end
|
38
|
+
|
39
|
+
def file_path
|
40
|
+
@file_path ||= (
|
41
|
+
NSFileManager.defaultManager.createDirectoryAtPath(self.class.base_path,
|
42
|
+
withIntermediateDirectories: true,
|
43
|
+
attributes: nil,
|
44
|
+
error: nil)
|
45
|
+
"#{self.class.base_path}#{self.filename}"
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.clear_files
|
50
|
+
fm = NSFileManager.defaultManager
|
51
|
+
fm.contentsOfDirectoryAtPath(self.base_path, error:nil).each do |filename|
|
52
|
+
fm.removeItemAtPath("#{self.base_path}#{filename}", error: nil)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def onPrepareForRelease
|
57
|
+
@events = nil
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
include PlasticCup
|
2
|
+
module Tgios
|
3
|
+
module LoadingIndicator
|
4
|
+
def add_loading_indicator_to(view)
|
5
|
+
Base.add_style_sheet(:loading_view, {
|
6
|
+
backgroundColor: :black.uicolor,
|
7
|
+
alpha: 0.5
|
8
|
+
})
|
9
|
+
@loading_view = Base.style(UIView.new, hidden: true)
|
10
|
+
base_view = Base.style(UIView.new, :loading_view)
|
11
|
+
@indicator = UIActivityIndicatorView.large
|
12
|
+
[{super_view: view, subview: @loading_view}, {super_view: @loading_view, subview: base_view}, {super_view: @loading_view, subview: @indicator}].each do |hash|
|
13
|
+
Motion::Layout.new do |l|
|
14
|
+
l.view hash[:super_view]
|
15
|
+
l.subviews 'subview' => hash[:subview]
|
16
|
+
l.vertical '|[subview]|'
|
17
|
+
l.horizontal '|[subview]|'
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@label = Base.style(UILabel.new,
|
21
|
+
frame: view.bounds,
|
22
|
+
font: lambda {UIFont.systemFontOfSize(22)},
|
23
|
+
textAlignment: :center.uialignment,
|
24
|
+
backgroundColor: :clear.uicolor,
|
25
|
+
textColor: :white.uicolor)
|
26
|
+
@label.sizeToFit
|
27
|
+
Motion::Layout.new do |l|
|
28
|
+
l.view @loading_view
|
29
|
+
l.subviews 'label' => @label
|
30
|
+
l.vertical '[label]-290-|'
|
31
|
+
l.horizontal '|-20-[label]-20-|'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def start_loading(text='')
|
36
|
+
@label.text = text
|
37
|
+
self.view.endEditing(true)
|
38
|
+
@loading_view.hidden = false
|
39
|
+
@indicator.startAnimating
|
40
|
+
end
|
41
|
+
|
42
|
+
def stop_loading
|
43
|
+
@loading_view.hidden = true
|
44
|
+
@indicator.stopAnimating
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Tgios
|
2
|
+
module ModelErrorsHelper
|
3
|
+
def has_error_in_fields_and_errors_for_name(fields, errors, field_name)
|
4
|
+
if errors.is_a?(Hash)
|
5
|
+
errors.has_key?(field_name) || errors.has_key?(related_name_in_fields(fields, field_name))
|
6
|
+
else
|
7
|
+
false
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def related_name_in_fields(fields, field_name)
|
12
|
+
field = fields.find{|fld| fld[:name] == field_name}
|
13
|
+
if field.is_a?(Hash)
|
14
|
+
field[:related_name]
|
15
|
+
else
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tgios
|
2
|
+
class NSDateHelper
|
3
|
+
def self.to_nsdate(date_string)
|
4
|
+
@formatter ||= (
|
5
|
+
@formatter = NSDateFormatter.new
|
6
|
+
@formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
|
7
|
+
@formatter.timeZone = NSTimeZone.timeZoneForSecondsFromGMT(0)
|
8
|
+
@formatter
|
9
|
+
)
|
10
|
+
date = @formatter.dateFromString(date_string)
|
11
|
+
if date.nil?
|
12
|
+
@formatter2 ||= (
|
13
|
+
@formatter2 = NSDateFormatter.new
|
14
|
+
@formatter2.dateFormat = "yyyy-MM-dd HH:mm:ss"
|
15
|
+
@formatter2.timeZone = NSTimeZone.timeZoneForSecondsFromGMT(0)
|
16
|
+
@formatter2
|
17
|
+
)
|
18
|
+
date = @formatter2.dateFromString(date_string).utc
|
19
|
+
|
20
|
+
else
|
21
|
+
date.utc
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Tgios
|
2
|
+
class PhotoController < ExtendedUIViewController
|
3
|
+
include LoadingIndicator
|
4
|
+
attr_accessor :url
|
5
|
+
|
6
|
+
def viewDidLoad
|
7
|
+
super
|
8
|
+
self.view.backgroundColor = :black.uicolor
|
9
|
+
|
10
|
+
add_loading_indicator_to(self.view)
|
11
|
+
unless @url.nil?
|
12
|
+
start_loading
|
13
|
+
image_loader = ImageLoader.new(@url)
|
14
|
+
image_loader.on(:image_loaded) do |image, success|
|
15
|
+
stop_loading
|
16
|
+
if success
|
17
|
+
ap 'image loaded'
|
18
|
+
# TODO: use motion layout or other way to handle frame size (or allow full screen)
|
19
|
+
small_frame = self.view.bounds
|
20
|
+
small_frame.size.height -= 20 + 44 if small_frame == UIScreen.mainScreen.bounds
|
21
|
+
|
22
|
+
scroll_view = PhotoScrollView.alloc.initWithFrame(small_frame, image: image)
|
23
|
+
self.view.addSubview(scroll_view)
|
24
|
+
end
|
25
|
+
image_loader.prepareForRelease
|
26
|
+
end
|
27
|
+
image_loader.load
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def onPrepareForRelease
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Tgios
|
2
|
+
class PhotoScrollView < UIScrollView
|
3
|
+
MAX_SCALE = 4.0
|
4
|
+
|
5
|
+
def initWithFrame(frame, image: image)
|
6
|
+
initWithFrame(frame)
|
7
|
+
ap "init #{self.class.name}"
|
8
|
+
self.showsVerticalScrollIndicator = false
|
9
|
+
self.showsHorizontalScrollIndicator = false
|
10
|
+
self.bouncesZoom = true
|
11
|
+
self.decelerationRate = UIScrollViewDecelerationRateFast
|
12
|
+
self.delegate = self
|
13
|
+
self.backgroundColor = :clear.uicolor
|
14
|
+
|
15
|
+
page_rect = CGRectMake(0, 0, image.size.width, image.size.height)
|
16
|
+
|
17
|
+
img_scale = frame.size.width / page_rect.size.width
|
18
|
+
fit_scale = frame.size.height / page_rect.size.height
|
19
|
+
fit_scale = img_scale if img_scale < fit_scale
|
20
|
+
|
21
|
+
page_rect.size = CGSizeMake(page_rect.size.width * img_scale, page_rect.size.height * img_scale)
|
22
|
+
|
23
|
+
@image_view = PlasticCup::Base.style(UIImageView.new,
|
24
|
+
image: image,
|
25
|
+
frame: page_rect,
|
26
|
+
contentMode: UIViewContentModeScaleAspectFit)
|
27
|
+
self.addSubview(@image_view)
|
28
|
+
|
29
|
+
self.zoomScale = 0.995 if page_rect.size.height > frame.size.height
|
30
|
+
|
31
|
+
self.maximumZoomScale = MAX_SCALE / img_scale
|
32
|
+
self.minimumZoomScale = fit_scale / img_scale
|
33
|
+
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def layoutSubviews
|
38
|
+
super
|
39
|
+
|
40
|
+
bsize = self.bounds.size
|
41
|
+
center_frame = @image_view.frame
|
42
|
+
|
43
|
+
center_frame.origin.x = center_frame.size.width < bsize.width ? (bsize.width - center_frame.size.width) / 2.0 : 0
|
44
|
+
center_frame.origin.y = center_frame.size.height < bsize.height ? (bsize.height - center_frame.size.height) / 2.0 : 0
|
45
|
+
|
46
|
+
@image_view.frame = center_frame
|
47
|
+
@image_view.contentScaleFactor = 1.0
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def viewForZoomingInScrollView(scrollView)
|
52
|
+
@image_view
|
53
|
+
end
|
54
|
+
|
55
|
+
def dealloc
|
56
|
+
ap "dealloc #{self.class.name}"
|
57
|
+
super
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|