Swift - Custom control for UISlider - ios

I already created a custom control for UISlider. This UISlider have 2 thumbs so it can find minimum and maximum values. I take these tutorials as my references.
http://www.raywenderlich.com/76433/how-to-make-a-custom-control-swift
http://www.sitepoint.com/wicked-ios-range-slider-part-one/
Compiled successfully but my UISlider looks weird. You can take a look my github repository here.
https://github.com/datomnurdin/RevivalxSwiftSlider
My custom control for UISlider
import UIKit
class RangeSlider: UIControl {
var minimumValue: Float!
var maximumValue: Float!
var minimumRange: Float!
var selectedMinimumValue: Float!
var selectedMaximumValue: Float!
var distanceFromCenter: Float!
var _padding: Float!
var _maxThumbOn: Bool!
var _minThumbOn: Bool!
var _minThumb = UIImageView()
var _maxThumb = UIImageView()
var _track = UIImageView()
var _trackBackground = UIImageView()
override init(frame: CGRect){
super.init(frame: frame)
_minThumbOn = false;
_maxThumbOn = false;
_padding = 20;
let _trackBackground = UIImageView(image: UIImage(named: "bar-background.png"))
_trackBackground.center = self.center;
self.addSubview(_trackBackground)
let _track = UIImageView(image: UIImage(named: "bar-highlight.png"))
_track.center = self.center
self.addSubview(_track)
let _minThumb = UIImageView(image: UIImage(named: "handle.png"), highlightedImage: UIImage(named: "handle-hover.png"))
_minThumb.frame = CGRectMake(0,0, self.frame.size.height,self.frame.size.height);
_minThumb.contentMode = UIViewContentMode.Center
self.addSubview(_minThumb)
let _maxThumb = UIImageView(image: UIImage(named: "handle.png"), highlightedImage: UIImage(named: "handle-hover.png"))
_maxThumb.frame = CGRectMake(0,0, self.frame.size.height,self.frame.size.height)
_maxThumb.contentMode = UIViewContentMode.Center
self.addSubview(_maxThumb)
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func layoutSubviews() {
_minThumb.center = CGPointMake(CGFloat(self.xForValue(selectedMinimumValue)), self.center.y);
_maxThumb.center = CGPointMake(CGFloat(self.xForValue(selectedMaximumValue)), self.center.y)
NSLog("Tapable size %f", _minThumb.bounds.size.width);
self.updateTrackHighlight()
}
func xForValue(value: Float) -> Float {
return (Float(self.frame.size.width) - (_padding * 2)) * ((value - minimumValue) / (maximumValue - minimumValue)) + _padding
}
func valueForX(x: Float) -> Float {
return minimumValue + (x - _padding) / (Float(self.frame.size.width) - (_padding * 2)) * (maximumValue - minimumValue)
}
func continueTrackingWithTouch(touch: UITouch, event: UIEvent) -> Bool {
if !_minThumbOn && !_maxThumbOn {
return true
}
var touchPoint: CGPoint = touch.locationInView(self)
if (_minThumbOn != nil) {
_minThumb.center = CGPointMake(max(CGFloat(self.xForValue(minimumValue)),min(touchPoint.x - CGFloat(distanceFromCenter), CGFloat(self.xForValue(selectedMaximumValue - minimumRange)))),CGFloat(_minThumb.center.y))
selectedMinimumValue = self.valueForX(Float(_minThumb.center.x))
}
if (_maxThumbOn != nil) {
_maxThumb.center = CGPointMake(min(CGFloat(self.xForValue(maximumValue)), max(touchPoint.x - CGFloat(distanceFromCenter), CGFloat(self.xForValue(selectedMinimumValue + minimumRange)))), CGFloat(_maxThumb.center.y))
selectedMaximumValue = self.valueForX(Float(_maxThumb.center.x))
}
self.updateTrackHighlight()
self.setNeedsLayout()
self.sendActionsForControlEvents(UIControlEvents.ValueChanged)
return true
}
func beginTrackingWithTouch(touch: UITouch, event: UIEvent) -> Bool {
var touchPoint: CGPoint = touch.locationInView(self)
if(CGRectContainsPoint(_minThumb.frame, touchPoint)){
_minThumbOn = true;
distanceFromCenter = Float(touchPoint.x - _minThumb.center.x)
}
else if(CGRectContainsPoint(_maxThumb.frame, touchPoint)){
_maxThumbOn = true;
distanceFromCenter = Float(touchPoint.x - _maxThumb.center.x)
}
return true;
}
func endTrackingWithTouch(touch: UITouch, event: UIEvent) {
_minThumbOn = false;
_maxThumbOn = false;
}
func updateTrackHighlight() {
_track.frame = CGRectMake(
_minThumb.center.x,
_track.center.y - (_track.frame.size.height/2),
_maxThumb.center.x - _minThumb.center.x,
_track.frame.size.height
);
}
}
My output
It suppose to be like this
How can I fix this?

Your problem lies at the 'let'.
_minThumb = UIImageView(image: UIImage(named: "handle.png"), highlightedImage: UIImage(named: "handle-hover.png"))
_minThumb.frame = CGRectMake(0,0, self.frame.size.height,self.frame.size.height);
_minThumb.contentMode = UIViewContentMode.Center
self.addSubview(_minThumb)
_maxThumb = UIImageView(image: UIImage(named: "handle.png"), highlightedImage: UIImage(named: "handle-hover.png"))
_maxThumb.frame = CGRectMake(0,0, self.frame.size.height,self.frame.size.height)
_maxThumb.contentMode = UIViewContentMode.Center
self.addSubview(_maxThumb)
Just remove the 'let' and it should work, this is because you are using _minThumb and _maxThumb globally, but you declared both as a constant, so they would not respond to any changes once initalized.

Related

How to Animate UIView on a line path without using UIBezierPath?

I have draw UIView say rectangle(player) and line, using context using draw(_ rect: CGRect) method.
I want to move the player on a line when clicking the play button.
I have searched lots of, but I found only for UIBezierpath but I want to use it with context
and I have tried some code name with playAnimation function.
class CanvasView : UIView{
// straight line
var lineStartPoint = CGPoint()
var lineEndpoint = CGPoint()
var lineStrokeList = [Lines]()
var lineDict : [String : Lines] = [:]
var lineActionDictionary : [String : [String : Lines]] = [:]
var linesPath : CGPath?
//player
var playerPoint : CGPoint?
var playerList = [Players]()
var playerDict : [Int : Players] = [:]
var selectedPlayerNumber : Int = 0
//action
var actionDict : [String : String] = ["1" : "1"]
var actionKeyIndex = String()
var actionList : [String] = [String]()
// common
var strokeWidth : CGFloat = 2.0
var currentColor = UIColor.black.cgColor
var imageSize : CGFloat = 30.0
private var selectedShape : ShapeType = .player
//MARK: Touches Began
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
switch selectedShape {
case .player:
playerPoint = touch.location(in: self)
playerDict.forEach { (key,value) in
if value.rect.contains(playerPoint ?? CGPoint()){
selectedPlayerNumber = Int(key)
self.playerDict.removeValue(forKey: selectedPlayerNumber)
return
}
}
setNeedsDisplay()
case .line:
let pos = touch.location(in: self)
lineStartPoint = pos
lineEndpoint = pos
setNeedsDisplay()
}
}
//MARK: Touches Moved
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let isfreeDrawSelected = isfreeDrawSelected
switch selectedShape {
case .player:
playerPoint = touch.location(in: self)
setNeedsDisplay()
case .line:
lineEndpoint = touch.location(in: self)
setNeedsDisplay()
}
}
//MARK: Touches Ended
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
switch selectedShape {
case .player:
if playerPoint != nil {
let point = CGPoint(x: playerPoint!.x, y: playerPoint!.y)
let x = playerPoint!.x - 15
let y = playerPoint!.y - 15
let playerRect = CGRect(x: x, y: y, width: imageSize, height: imageSize)
let playerstruct = Players(point: point,rect: playerRect)
playerList.append(playerstruct)
playerDict[selectedPlayerNumber] = playerstruct
playerPoint = nil
selectedPlayerNumber += 1
setNeedsDisplay()
}
case .line:
lineEndpoint = touch.location(in: self)
if !playerDict.isEmpty{
if lineActionDictionary[actionKeyIndex]?[String(selectedPlayerNumber)] != nil{
let alert = UIAlertController(title: "", message: "You can't add mutiple lines for action \(actionKeyIndex)", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { alertAction in
debugPrint(self.lineActionDictionary)
}))
UIApplication.shared.windows
.last?.rootViewController?
.present(alert, animated: false, completion: nil)
}
else{
let line = Lines(startPoint: lineStartPoint, endPoint: lineEndpoint, color: currentColor, width: strokeWidth)
lineStrokeList.append(line)
lineDict[String(selectedPlayerNumber)] = line
if lineActionDictionary[actionKeyIndex] != nil {
lineActionDictionary[actionKeyIndex]!.updateValue(line, forKey: String(selectedPlayerNumber))
}else{
lineActionDictionary[actionKeyIndex] = lineDict
}
}
lineStartPoint = CGPoint()
lineEndpoint = CGPoint()
lineDict.removeAll()
setNeedsDisplay()
}
}
}
//MARK: Draw
override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else { return }
if playerPoint != nil {
let x = (playerPoint?.x)! - 15
let y = (playerPoint?.y)! - 15
let player = CGRect(x: x, y: y, width: imageSize, height: imageSize)
context.addRect(player)
context.drawPath(using: .stroke)
linesPath = context.path
}
if !playerDict.isEmpty{
playerDict.forEach { (key,value) in
let x = value.point.x - 15
let y = value.point.y - 15
let player = CGRect(x: x, y: y, width: imageSize, height: imageSize)
let colorView = UIView(frame: player)
context.addRect(player)
context.drawPath(using: .stroke)
}
}
if lineStartPoint.x > 0 && lineEndpoint.y > 0{
context.setLineCap(.round)
context.setLineWidth(2.0)
context.move(to: lineStartPoint)
context.addLine(to: lineEndpoint)
context.setStrokeColor(UIColor.black.cgColor)
context.strokePath()
}
if !lineActionDictionary.isEmpty{
lineActionDictionary.forEach { (actionkey,actionvalue) in
actionvalue.forEach { (playerkey,playervalue) in
let startPoint = playervalue.startPoint
let endPoint = playervalue.endPoint
let color = playervalue.color
let width = playervalue.width
context.setLineCap(.round)
context.setLineWidth(width)
context.move(to: startPoint)
context.addLine(to: endPoint)
context.setStrokeColor(color)
context.strokePath()
}
}
}
}
func selectedShape(selctedShapes : ShapeTypes) {
selectedShape = selctedShapes
}
func playAnimation(){
switch selectedShape {
case .player:
break
case .line:
let player = playerList.first
let views = UIView(frame: player!.rect)
views.alpha = 1.0
UIView.animate(withDuration: 0.25, animations: {
views.frame = CGRect(x: 0, y: 10, width: 20, height: 20)
}, completion: {
(value: Bool) in
debugPrint(">>> Animation done.")
})
}
}
}
#ShapeTypes Enum
enum ShapeTypes : Int {
case player = 0
case line
}
#UIViewController
#available(iOS 14.0, *)
class TestVC : UIViewController{
#IBOutlet weak var colorPicker: UIImageView!
#IBOutlet weak var sliderMaxValueLbl: UILabel!
#IBOutlet weak var radiusSlider: UISlider!
#IBOutlet weak var tv: CanvasView!
#IBOutlet weak var shapeSegment : UISegmentedControl!
#IBOutlet weak var playerFormationPickerDoneBtn: UIButton!
#IBOutlet weak var playerFormationPicker: UIPickerView!
#IBOutlet weak var playerFormationTF: UITextField!
#IBOutlet weak var actionPickerDoneBtn: UIButton!
#IBOutlet weak var actionPicker: UIPickerView!
#IBOutlet weak var actionPickerTF: UITextField!
#IBOutlet weak var addActionBtn: UIBarButtonItem!
//playerformation pickerview
var playerFormationPickerData = String()
var playerFormationPickerSelectedIndex = Int()
var isPlayerFormationSelected : Bool = false
var playerViewArray: [PlayerView] = []
var pickerViewListData: [String] = [String]()
//action pickerview
var actionPickerData = String()
var actionPickerSelectedIndex = Int()
#IBAction func actionPickerDoneButtonAction(_ sender: UIButton) {
actionPickerDoneBtn.isHidden = true
actionPicker.isHidden = true
actionPickerTF.text = actionPickerData
tv.actionKeyIndex = actionPickerTF.text?.components(separatedBy: " ").last ?? ""
debugPrint(tv.actionKeyIndex)
}
func setSliderThumb(_ color: UIColor, _ width : CGFloat) {
let circleImage = makeCircleWith(size: CGSize(width: width, height: width),
backgroundColor: color)
radiusSlider.setThumbImage(circleImage, for: .normal)
radiusSlider.setThumbImage(circleImage, for: .highlighted)
}
fileprivate func makeCircleWith(size: CGSize, backgroundColor: UIColor) -> UIImage? {
UIGraphicsBeginImageContextWithOptions(size, false, 0.0)
let context = UIGraphicsGetCurrentContext()
context?.setFillColor(backgroundColor.cgColor)
context?.setStrokeColor(UIColor.black.cgColor)
let bounds = CGRect(origin: .zero, size: size)
context?.addEllipse(in: bounds)
context?.drawPath(using: .fill)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
return image
}
#IBAction func radiusSliderAction(_ sender: UISlider) {
let radius = Int(radiusSlider.value)
tv.radius = CGFloat(radius)
sliderMaxValueLbl.text = String(radius)
}
#IBAction func playAnimationAction(_ sender: UIBarButtonItem) {
tv.playAnimation()
}
override func viewDidLoad() {
super.viewDidLoad()
radiusSlider.minimumValue = 30
radiusSlider.maximumValue = 100
radiusSlider.value = 0
sliderMaxValueLbl.text = String(Int(radiusSlider.value))
setSliderThumb(UIColor.white, 20)
if let img = UIImage(named: "ground") { //background image
UIGraphicsBeginImageContext(tv.frame.size)
img.draw(in: tv.bounds)
let image = UIGraphicsGetImageFromCurrentImageContext()
UIGraphicsEndImageContext()
if let image = image {
tv.backgroundColor = UIColor(patternImage: image)
tv.contentMode = .scaleAspectFill
}
}
actionPicker.delegate = self
actionPicker.dataSource = self
actionPicker.isHidden = true
actionPickerTF.delegate = self
actionPickerTF.textAlignment = .center
actionPickerTF.placeholder = "Select action"
actionPickerDoneBtn.isHidden = true
actionPickerDoneBtn.tintColor = .systemBlue
tv.actionKeyIndex = tv.actionDict["1"] ?? ""
tv.actionList = ["action \(tv.actionKeyIndex)"]
actionPickerData = tv.actionList[0]
actionPickerTF.text = actionPickerData
tv.currentColor = UIColor.black.cgColor
}
#IBAction func addActionButton(_ sender: UIBarButtonItem) {
let count = tv.actionList.count + 1
let element = tv.lineActionDictionary.keys.max()
// let element = tv.actionDict.values.max() // find max dict value
guard let maxValue = element?.first?.wholeNumberValue else { return }
debugPrint(maxValue)
tv.actionKeyIndex = String(maxValue + 1)
actionPickerTF.text = "action \(tv.actionKeyIndex)"
tv.actionList.append("action \(tv.actionKeyIndex)")
actionPicker.reloadAllComponents()
}
#IBAction func selectShapeSegmentAction(_ sender: UISegmentedControl) {
let selectShape : ShapeType = ShapeTypes(rawValue: shapeSegment.selectedSegmentIndex) ?? .player
tv.selectedShape(selctedShape: selectShape)
}
}
#available(iOS 14.0, *)
extension TestVC : UIPickerViewDelegate {
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
if pickerView == actionPicker{
actionPickerData = tv.actionList[row]
actionPickerTF.text = actionPickerData
actionPickerSelectedIndex = actionPicker.selectedRow(inComponent: 0)
}
}
}
#available(iOS 14.0, *)
extension TestVC : UIPickerViewDataSource {
func numberOfComponents(in pickerView: UIPickerView) -> Int {
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
return tv.actionList.count
}
func pickerView(_ pickerView: UIPickerView, titleForRow row: Int, forComponent component: Int) -> String? {
return tv.actionList[row]
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
var title = UILabel()
if let view = view {
title = view as! UILabel
}
if pickerView == actionPicker {
title.text = tv.actionList[row]
}
title.font = UIFont.systemFont(ofSize: 16, weight: UIFont.Weight.medium)
title.textColor = UIColor.black
title.textAlignment = .center
return title
}
}
Lets say you have some customView and you want to simply temporary move it a little bit down. Here is lightweight solution.
let customView = UIView(frame: CGRect(x: 0, y: 0, width: 200, height: 200))
func animateGoingDown() {
UIView.animate(withDuration: 0.5) {
self.customView.transform = CGAffineTransform(translationX: 0, y: 200)
}
}
func animateGoingToOriginalPosition() {
UIView.animate(withDuration: 0.5) {
self.customView.transform = .identity
}
}
Howefully this is what you are searching for. If you have some questions, do not hesitate to ask.

ios swift / how to apply one gesture on one image to all images in UITableView

I am trying to construct shperical view application(same with panorama view) with ios.
there are listed images(paranonma) in UITableView.
you can view each image as 360 degrees using both device motion and finger gesture.
But, is it possible that one gesture at any image leads all image to take same effect of the gesture?
for example, if I circulate top image by finger, all image "also" circulates same with the top image.
"Bubbli" application has that function.
I tried to put pangesturerecognizer as global variable to shared gesture, but It didn't work.
How can i..?
it's tableview code.
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "pic_cell", for: indexPath) as! pic_TableViewCell
let tmp = tmp_list[indexPath.row]
cell.pic_View.loadPanoramaView(image: tmp)
return cell
}
it's tableviewcell_uiview code.
class TableViewCell_UIView: UIView {
var image_name:String = ""
lazy var device: MTLDevice = {
guard let device = MTLCreateSystemDefaultDevice() else {
fatalError("Failed to create MTLDevice")
}
return device
}()
weak var panoramaView: PanoramaView?
func loadPanoramaView(image: String) {
#if arch(arm) || arch(arm64)
let panoramaView = PanoramaView(frame: view.bounds, device: device)
#else
let panoramaView = PanoramaView(frame: self.bounds) // iOS Simulator
#endif
panoramaView.setNeedsResetRotation()
panoramaView.translatesAutoresizingMaskIntoConstraints = false
self.addSubview(panoramaView)
// fill parent view
let constraints: [NSLayoutConstraint] = [
panoramaView.topAnchor.constraint(equalTo: self.topAnchor),
panoramaView.bottomAnchor.constraint(equalTo: self.bottomAnchor),
panoramaView.leadingAnchor.constraint(equalTo: self.leadingAnchor),
panoramaView.trailingAnchor.constraint(equalTo: self.trailingAnchor)
]
NSLayoutConstraint.activate(constraints)
// double tap to reset rotation
let doubleTapGestureRecognizer = UITapGestureRecognizer(target: panoramaView, action: #selector(PanoramaView.setNeedsResetRotation(_:)))
doubleTapGestureRecognizer.numberOfTapsRequired = 2
panoramaView.addGestureRecognizer(doubleTapGestureRecognizer)
self.panoramaView = panoramaView
panoramaView.load(UIImage(named: image)!, format: .mono)
}
}
it's PanoramaView.swift which defines PanoramaView class.
final class PanoramaView: UIView, SceneLoadable {
#if (arch(arm) || arch(arm64)) && os(iOS)
public let device: MTLDevice
#endif
public var scene: SCNScene? {
get {
return scnView.scene
}
set(value) {
orientationNode.removeFromParentNode()
value?.rootNode.addChildNode(orientationNode)
scnView.scene = value
}
}
public weak var sceneRendererDelegate: SCNSceneRendererDelegate?
public lazy var orientationNode: OrientationNode = {
let node = OrientationNode()
let mask = CategoryBitMask.all.subtracting(.rightEye)
node.pointOfView.camera?.categoryBitMask = mask.rawValue
return node
}()
lazy var scnView: SCNView = {
#if (arch(arm) || arch(arm64)) && os(iOS)
let view = SCNView(frame: self.bounds, options: [
SCNView.Option.preferredRenderingAPI.rawValue: SCNRenderingAPI.metal.rawValue,
SCNView.Option.preferredDevice.rawValue: self.device
])
#else
let view = SCNView(frame: self.bounds)
#endif
view.backgroundColor = .black
view.isUserInteractionEnabled = false
view.delegate = self
view.pointOfView = self.orientationNode.pointOfView
view.isPlaying = true
self.addSubview(view)
return view
}()
// to integrated panGesture
fileprivate lazy var panGestureManager: PanoramaPanGestureManager = {
let manager = PanoramaPanGestureManager(rotationNode: self.orientationNode.userRotationNode)
manager.minimumVerticalRotationAngle = -60 / 180 * .pi
manager.maximumVerticalRotationAngle = 60 / 180 * .pi
return manager
}()
fileprivate lazy var interfaceOrientationUpdater: InterfaceOrientationUpdater = {
return InterfaceOrientationUpdater(orientationNode: self.orientationNode)
}()
#if (arch(arm) || arch(arm64)) && os(iOS)
public init(frame: CGRect, device: MTLDevice) {
self.device = device
super.init(frame: frame)
addGestureRecognizer(panGestureManager.gestureRecognizer) // modify
//addGestureRecognizer(setGestureRecognizer())
}
#else
public override init(frame: CGRect) {
super.init(frame: frame)
addGestureRecognizer(panGestureManager.gestureRecognizer) // modify
//addGestureRecognizer(setGestureRecognizer())
}
#endif
public required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
orientationNode.removeFromParentNode()
}
public override func layoutSubviews() {
super.layoutSubviews()
scnView.frame = bounds
}
public override func willMove(toWindow newWindow: UIWindow?) {
if newWindow == nil {
interfaceOrientationUpdater.stopAutomaticInterfaceOrientationUpdates()
} else {
interfaceOrientationUpdater.startAutomaticInterfaceOrientationUpdates()
interfaceOrientationUpdater.updateInterfaceOrientation()
}
}
}
extension PanoramaView: ImageLoadable {}
it's PanoramaPanGestureManager.swift
final class PanoramaPanGestureManager {
let rotationNode: SCNNode
var allowsVerticalRotation = true
var minimumVerticalRotationAngle: Float?
var maximumVerticalRotationAngle: Float?
var allowsHorizontalRotation = true
var minimumHorizontalRotationAngle: Float?
var maximumHorizontalRotationAngle: Float?
lazy var gestureRecognizer: UIPanGestureRecognizer = {
let recognizer = AdvancedPanGestureRecognizer()
recognizer.addTarget(self, action: #selector(handlePanGesture(_:)))
recognizer.earlyTouchEventHandler = { [weak self] in
self?.stopAnimations()
self?.resetReferenceAngles()
}
return recognizer
}()
private var referenceAngles: SCNVector3?
init(rotationNode: SCNNode) {
self.rotationNode = rotationNode
}
#objc func handlePanGesture(_ sender: UIPanGestureRecognizer) {
guard let view = sender.view else {
return
}
switch sender.state {
case .changed:
guard let referenceAngles = referenceAngles else {
break
}
var angles = SCNVector3Zero
let viewSize = max(view.bounds.width, view.bounds.height)
let translation = sender.translation(in: view)
if allowsVerticalRotation {
var angle = referenceAngles.x + Float(translation.y / viewSize) * (.pi / 2)
if let minimum = minimumVerticalRotationAngle {
angle = max(angle, minimum)
}
if let maximum = maximumVerticalRotationAngle {
angle = min(angle, maximum)
}
angles.x = angle
}
if allowsHorizontalRotation {
var angle = referenceAngles.y + Float(translation.x / viewSize) * (.pi / 2)
if let minimum = minimumHorizontalRotationAngle {
angle = max(angle, minimum)
}
if let maximum = maximumHorizontalRotationAngle {
angle = min(angle, maximum)
}
angles.y = angle
}
SCNTransaction.lock()
SCNTransaction.begin()
SCNTransaction.disableActions = true
rotationNode.eulerAngles = angles.normalized
SCNTransaction.commit()
SCNTransaction.unlock()
case .ended:
var angles = rotationNode.eulerAngles
let velocity = sender.velocity(in: view)
let viewSize = max(view.bounds.width, view.bounds.height)
if allowsVerticalRotation {
var angle = angles.x
angle += Float(velocity.y / viewSize) / .pi
if let minimum = minimumVerticalRotationAngle {
angle = max(angle, minimum)
}
if let maximum = maximumVerticalRotationAngle {
angle = min(angle, maximum)
}
angles.x = angle
}
if allowsHorizontalRotation {
var angle = angles.y
angle += Float(velocity.x / viewSize) / .pi
if let minimum = minimumHorizontalRotationAngle {
angle = max(angle, minimum)
}
if let maximum = maximumHorizontalRotationAngle {
angle = min(angle, maximum)
}
angles.y = angle
}
SCNTransaction.lock()
SCNTransaction.begin()
SCNTransaction.animationDuration = 1
SCNTransaction.animationTimingFunction = CAMediaTimingFunction(controlPoints: 0.165, 0.84, 0.44, 1)
rotationNode.eulerAngles = angles
SCNTransaction.commit()
SCNTransaction.unlock()
default:
break
}
}
func stopAnimations() {
SCNTransaction.lock()
SCNTransaction.begin()
SCNTransaction.disableActions = true
rotationNode.eulerAngles = rotationNode.presentation.eulerAngles.normalized
rotationNode.removeAllAnimations()
SCNTransaction.commit()
SCNTransaction.unlock()
}
private func resetReferenceAngles() {
referenceAngles = rotationNode.presentation.eulerAngles
}
}
private final class AdvancedPanGestureRecognizer: UIPanGestureRecognizer {
var earlyTouchEventHandler: (() -> Void)?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesBegan(touches, with: event)
if state == .possible {
earlyTouchEventHandler?()
}
}
}
private extension Float {
var normalized: Float {
let angle: Float = self
let π: Float = .pi
let π2: Float = π * 2
if angle > π {
return angle - π2 * ceil(abs(angle) / π2)
} else if angle < -π {
return angle + π2 * ceil(abs(angle) / π2)
} else {
return angle
}
}
}
private extension SCNVector3 {
var normalized: SCNVector3 {
let angles: SCNVector3 = self
return SCNVector3(
x: angles.x.normalized,
y: angles.y.normalized,
z: angles.z.normalized
)
}
}
how can I integrate all cell's gesturemanager..?

Can anybody help me understand this code - iOS swift

This is the code of creating a custom segmented control i found over the Internet. I have a problem in understanding the last two functions, beginTrackingWithTouch and layoutSubviews. What are the purpose of these functions and what does their code do exactly
and finally, excuse this question. I'm still a beginner in iOS development and I'm just seeking help.
#IBDesignable class CustomSegmentedControl : UIControl {
private var labels = [UILabel]()
var items = ["Item 1","Item 2"] {
didSet {
setUpLabels()
}
}
var thumbView = UIView()
var selectedIndex : Int = 0 {
didSet {
displayNewSelectedIndex()
}
}
override init(frame: CGRect) {
super.init(frame: frame)
setupView()
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
setupView()
}
func setupView() {
//layer.cornerRadius = frame.height / 2
layer.borderColor = UIColor.blackColor().CGColor
layer.borderWidth = 2
backgroundColor = UIColor.clearColor()
setUpLabels()
insertSubview(thumbView, atIndex: 0)
}
func setUpLabels() {
for label in labels {
label.removeFromSuperview()
}
labels.removeAll(keepCapacity: true)
for index in 1...items.count {
let label = UILabel(frame: CGRectZero)
label.text = items[index-1]
label.textAlignment = .Center
label.textColor = UIColor.blackColor()
self.addSubview(label)
labels.append(label)
}
}
func displayNewSelectedIndex() {
let label = labels[selectedIndex]
self.thumbView.frame = label.frame
}
override func layoutSubviews() {
super.layoutSubviews()
var selectFrame = self.bounds
let newWidth = CGRectGetWidth(selectFrame) / CGFloat(items.count)
selectFrame.size.width = newWidth
thumbView.frame = selectFrame
thumbView.backgroundColor = UIColor.grayColor()
//thumbView.layer.cornerRadius = thumbView.frame.height / 2
let labelHeight = self.bounds.height
let labelWidth = self.bounds.width / CGFloat(labels.count)
for index in 0..<labels.count {
let label = labels[index]
let xPosition = CGFloat(index) * labelWidth
label.frame = CGRectMake(xPosition, 0, labelWidth, labelHeight)
}
}
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
let location = touch.locationInView(self)
var calculatedIndex : Int?
for (index, item) in labels.enumerate() {
if item.frame.contains(location) {
calculatedIndex = index
}
if calculatedIndex != nil {
selectedIndex = calculatedIndex!
sendActionsForControlEvents(.ValueChanged)
}
}
return false
}
}
I can explain begin tracking method, the other one is researchable i think so
/*** beginTrackingWithTouch.. method to customize tracking. ***/
// Parameters : touch : this returns the touch that occurred at a certain point in the view. withEvent, returns the UIEvent
// associated with the touch.
// Return Type: Boolean.
override func beginTrackingWithTouch(touch: UITouch, withEvent event: UIEvent?) -> Bool {
let location = touch.locationInView(self) // This line returns the location of touch in the view. This location is in CGPoint.
var calculatedIndex : Int?
for (index, item) in labels.enumerate() { /// enumeration of an array gives you sequence type of integer and corresponding element type of the array.
if item.frame.contains(location) { /// so labels.enumerate returns the key value pairs like so : [(0, labelatIndex0), (1, labelatIndex1).. and so on.]
calculatedIndex = index /// here index is the key, and item is the value (label.)
// So for every label/index pair, you check whether the touch happened on the label by getting the frame of the label and checking if location is a part of the frame
/// You equate the index to calculatedIndex
}
// Now you if your calculated index is a valid number, you class, which extends UIControl, will call its method stating the change of value(sendActionsForControlEvents).
// This in turn, will send a message to all the targets that have been registered using addTarget:action:forControlEvents: method.
if calculatedIndex != nil {
selectedIndex = calculatedIndex!
sendActionsForControlEvents(.ValueChanged)
}
}
return false
}

How to set a tap for imageview array in Swift/Xcode

I am trying to set tap listeners for each imageview from my imageview array and print when one is selected. Ideally I would like each imageview to have its on listener - such as "imageview 2 was selected." Thanks!
class ViewController2: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
var index: Int = 0
var images: [UIImage] = [UIImage(named: "image1.png")!, UIImage(named: "image2.png")!, UIImage(named: "image3.png")!, UIImage(named: "image4.png")!, UIImage(named: "image5.png")!, UIImage(named: "image6.png")!, UIImage(named: "image7.png")!, UIImage(named: "image8.png")!, UIImage(named: "image9.png")!, UIImage(named: "image10.png")!, UIImage(named: "image11.png")!, UIImage(named: "image12.png")!, UIImage(named: "image13.png")!, UIImage(named: "image14.png")!, UIImage(named: "image15.png")!, UIImage(named: "image16.png")!, UIImage(named: "image17.png")!, UIImage(named: "image18.png")!, UIImage(named: "image19.png")!, UIImage(named: "image20.png")!, UIImage(named: "image21.png")!, UIImage(named: "image22.png")!, UIImage(named: "image23.png")!, UIImage(named: "image24.png")!, UIImage(named: "image25.png")!, UIImage(named: "image26.png")!, UIImage(named: "image27.png")!, UIImage(named: "image28.png")!, UIImage(named: "image29.png")!, UIImage(named: "image30.png")!]
var imageView: [UIImageView] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
//let screenHeight = screenSize.height
let numWidth: CGFloat = 3
let numHeight: CGFloat = 10
self.scrollView.frame.size.width = screenWidth
let width: CGFloat = (self.scrollView.frame.size.width - (numWidth + 1))/3
var tap = UITapGestureRecognizer(target: self, action: Selector("tappedMe"))
for var i:CGFloat = 0; i < 3; ++i{
for var j:CGFloat = 0; j < 10; ++j {
let image: UIImage = images[index]
imageView.append(UIImageView(image: image))
imageView[index].frame = CGRectMake(width*i, width*j, width, width)
self.scrollView.addSubview(imageView[index])
imageView[index].addGestureRecognizer(tap)
imageView[index].userInteractionEnabled = true
index++
}
}
scrollView.contentSize.height = (width)*numHeight
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func imageTapped(img: AnyObject)
{
// Your action
print("tapped")
}
}
you should use custom class instead of holding array of UIImageView.
import UIKit
class MyCustomImageView : UIImageView{
var myId : String = "";
override init(frame: CGRect){
super.init(frame: frame);
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
println("IM TOUCHED\(myId)");
}
}
.
.
.
var imageView: [MyCustomImageView] = []
and in your loop:
for var i:CGFloat = 0; i < 3; ++i{
for var j:CGFloat = 0; j < 10; ++j {
let image: UIImage = images[index]
imageView.append(MyCustomImageView(frame: CGRectMake(width*i, width*j, width, width)))
imageView[index].image = image;
self.scrollView.addSubview(imageView[index])
imageView[index].myId = "Image number \(index)";
index++
}
}
and btw, dont know what are you trying to do with all width height,
but this is a bit more clear way:
class ViewController2: UIViewController {
#IBOutlet weak var scrollView: UIScrollView!
let imageIndex : (begin:Int,end:Int) = (begin:1,end:30);
var imageView: [MyCustomImageView] = []
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let screenSize: CGRect = UIScreen.mainScreen().bounds
let screenWidth = screenSize.width
//let screenHeight = screenSize.height
let numWidth: CGFloat = 3
let numHeight: CGFloat = 10
self.scrollView.frame.size.width = screenWidth
let width: CGFloat = (self.scrollView.frame.size.width - (numWidth + 1))/3
var index = self.imageIndex.begin;
for var i:CGFloat = 0; i < 3; ++i{
for var j:CGFloat = 0; j < 10; ++j {
let image: UIImage? = UIImage(named: "image\(index++).png");
var currentImageView = MyCustomImageView(frame: CGRectMake(width*i, width*j, width, width))
currentImageView.image = image;
currentImageView.userInteractionEnabled = true
currentImageView.myId = "image\(index++).png";
self.scrollView.addSubview(imageView[index])
imageView.append(currentImageView);
}
}
scrollView.contentSize.height = (width)*numHeight
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}

UIPageControl custom class - found nil changing image to the dots

I need to implement a UIPageControl with custom images instead the normal dot. So I create a custom class and connect it through the storyboard.
Since ios7 the subview of UIPageControl contain UIView instead of UIImageView. The subviews of the resulting UIView(UIIpageControl subviews) doesn't contain any subviews so I receive the error:
fatal error: unexpectedly found nil while unwrapping an Optional value.
Where I might have been wrong?
class WhitePageControl:UIPageControl{
let activeImage = UIImage(named: "dot_white")
let inactiveImage = UIImage(named: "dot_white_e")
override init(frame: CGRect){
super.init(frame: frame)
}
required init(coder aDecoder: NSCoder){
super.init(coder: aDecoder)
}
func updateDots(){
println(self.subviews.count) // 3
println(self.numberOfPages) // 3
for var index = 0; index < self.subviews.count; index++ {
println(index)
var dot:UIImageView!
var dotView:UIView = self.subviews[index] as UIView
println("1")
for subview in dotView.subviews{ // NIL HERE
println("2")
if subview.isKindOfClass(UIImageView){
println("3")
dot = subview as UIImageView
if index == self.currentPage{ dot.image = activeImage }
else{ dot.image = inactiveImage }
}
}
}
}
func setCurrentPage(value:Int){
super.currentPage = value
self.updateDots()
}
}
It's my solution:
import Foundation
class PageControl: UIPageControl {
var activeImage: UIImage!
var inactiveImage: UIImage!
override var currentPage: Int {
//willSet {
didSet { //so updates will take place after page changed
self.updateDots()
}
}
convenience init(activeImage: UIImage, inactiveImage: UIImage) {
self.init()
self.activeImage = activeImage
self.inactiveImage = inactiveImage
self.pageIndicatorTintColor = UIColor.clearColor()
self.currentPageIndicatorTintColor = UIColor.clearColor()
}
func updateDots() {
for var i = 0; i < count(subviews); i++ {
var view: UIView = subviews[i] as! UIView
if count(view.subviews) == 0 {
self.addImageViewOnDotView(view, imageSize: activeImage.size)
}
var imageView: UIImageView = view.subviews.first as! UIImageView
imageView.image = self.currentPage == i ? activeImage : inactiveImage
}
}
// MARK: - Private
func addImageViewOnDotView(view: UIView, imageSize: CGSize) {
var frame = view.frame
frame.origin = CGPointZero
frame.size = imageSize
var imageView = UIImageView(frame: frame)
imageView.contentMode = UIViewContentMode.Center
view.addSubview(imageView)
}
}
change setCurrentPage to Current Page ,bcoz that one is Obj C property
func currentPage(page: Int) {
super.currentPage = page
self.updateDots()
}
try this.

Resources