tgios 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|