#StackBounty: #ios #swift #uitextview Change UITextView text by using its tag number to identify the element due for change

Bounty: 50

//UITextView Creation
let textarea  = UITextView (frame : CGRect (x: 40, y: 100 ,width:100 , height:100))
                                            textarea.delegate = self
                                            textarea.tag = self.numarr
                                            textarea.backgroundColor = UIColor(red: 0.9686, green: 0.9686, blue: 0.9686, alpha: 1.0)
                                            textarea.layer.cornerRadius = 20.0
                                            textarea.contentInset = UIEdgeInsetsMake(5, 5, 5, 5);
                                            textarea.layer.masksToBounds = true
                                            self.scrollviewxp.addSubview(textarea)   

//Later after, the button function
 @IBAction func loadbutton(_ sender: Any) {

if let hellatextview = self.scrollviewxp.viewWithTag(index) as? UITextView
                        {

                            hellatextview.text = "success"
                        }

                                         }

The above code is not flagged as an error on Xcode but doesn’t change the UITextView (hellatextview) value upon execution. A textView with the tag number (index) exists but isn’t being changed.

Any ideas why it isn’t working? Ive had the same issue with UIButtons


Get this bounty!!!

#StackBounty: #object-oriented #design-patterns #swift #ios Swift/iOS component for label with clickable text buttons

Bounty: 100

So I’m creating a component based on UILabel, which I’m calling RichLabel. The main goal is to add support for clickable links (multiple). It should also be possible to to differentiate between types of links, so I can handle them differently. For instance one link should open ModalWindowA and another ModalWindowB.

I have something that works, but it’s not a very solid solution, and would love some input on the design.

RichButton

RichButton is the different types of button/links I currently support in the RichText.

protocol RichButton {
  var id: String { get }
  var buttonTitle: String { get }
}

struct ProfileRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

struct AttendeesRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

struct MeetingRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

RichLabelComponent

RichLabelComponent is holding the data for the RichLabelComponentView and formats the text and highlights the clickable text.

protocol Component {
  var id: String { get }
}

struct RichLabelComponent: Component {

  let id: String
  let text: String
  let buttons: [RichButton]

  var formattedText: String {
    let buttonTitles = buttons.map { $0.buttonTitle }
    let formattedString = String(format: text, arguments: buttonTitles)
    return formattedString
  }

  var attributedText: NSAttributedString? {
    let attributedText = NSMutableAttributedString(string: formattedText)
    let nsFormattedText = NSString(string: formattedText)
    attributedText.setAttributes([.font: Theme.regular(size: .large)], range: formattedText.whole)
    for button in buttons {
      let range = nsFormattedText.range(of: button.buttonTitle)
      let attributedButtonTitle = NSAttributedString(string: button.buttonTitle, attributes: [.foregroundColor: Theme.secondaryOrangeColor, .font: Theme.regular(size: .large)])
      attributedText.replaceCharacters(in: range, with: attributedButtonTitle)
    }
    return attributedText
  }

  init(id: String = UUID().uuidString, text: String) {
    self.id = id
    self.text = text
    self.buttons = []
  }

  init(id: String = UUID().uuidString, text: String, buttons: RichButton...) {
    self.id = id
    self.text = text
    self.buttons = buttons
  }
}

RichLabelComponentView

This is the view responsible for displaying the formatted label, and handles the tapGesture.

protocol RichLabelComponentViewDelegate: class {
  func richLabelComponentView(_ richLabelComponentView:   RichLabelComponentView, didTapButton button: RichButton, forComponent  component: RichLabelComponent)
}

class RichLabelComponentView: UIView {

  // MARK: - Internal properties

  private lazy var label: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.lineBreakMode = .byWordWrapping
    label.textAlignment = .left
    label.isUserInteractionEnabled = true
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
  }()

  private lazy var tapGestureRecognizer: UITapGestureRecognizer = {
    let tapGestureRecognizer = UITapGestureRecognizer()
    tapGestureRecognizer.addTarget(self, action: #selector(tapHandler(gesture:)))
    return tapGestureRecognizer
  }()

  // MARK: - External properties

  weak var delegate: RichLabelComponentViewDelegate?

  var component: RichLabelComponent? {
    didSet {
      label.attributedText = component?.attributedText
    }
  }

  // MARK: - Setup

  override init(frame: CGRect) {
    super.init(frame: frame)
    self.addGestureRecognizer(tapGestureRecognizer)
    self.addSubviewsAndConstraints()
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  private func addSubviewsAndConstraints() {
    addSubview(label)

    label.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
    label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
  }

  // MARK: - Actions

  @objc func tapHandler(gesture: UITapGestureRecognizer) {
    guard let component = component, let delegate = delegate else {
      return
    }

    for button in component.buttons {
      let nsString = component.formattedText as NSString
      let range = nsString.range(of: button.buttonTitle)
      if tapGestureRecognizer.didTapAttributedText(label: label, inRange: range) {
        delegate.richLabelComponentView(self, didTapButton: button, forComponent: component)
      }
    }
  }
}

This is the implementation which recognises if one of the text links/buttons where tapped:

extension UIGestureRecognizer {

  /**
   Returns `true` if the tap gesture was within the specified range of the attributed text of the label.

   - Parameter label:   The label to match tap gestures in.
   - Parameter targetRange: The range for the substring we want to match tap against.

   - Returns: A boolean value indication wether substring where tapped or not.
   */
  func didTapAttributedText(label: UILabel, inRange targetRange: NSRange) -> Bool {
    guard let attributedString = label.attributedText else { fatalError("Not able to fetch attributed string.") }

    var offsetXDivisor: CGFloat
    switch label.textAlignment {
    case .center: offsetXDivisor = 0.5
    case .right: offsetXDivisor = 1.0
    default: offsetXDivisor = 0.0
    }

    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(size: .zero)
    let textStorage = NSTextStorage(attributedString: attributedString)
    let labelSize = label.bounds.size

    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)

    textContainer.lineFragmentPadding = 0.0
    textContainer.lineBreakMode = label.lineBreakMode
    textContainer.maximumNumberOfLines = label.numberOfLines
    textContainer.size = labelSize

    let locationOfTouchInLabel = self.location(in: label)
    let textBoundingBox = layoutManager.usedRect(for: textContainer)

    let offsetX = (labelSize.width - textBoundingBox.size.width) * offsetXDivisor - textBoundingBox.origin.x
    let offsetY = (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
    let textContainerOffset = CGPoint(x: offsetX, y: offsetY)

    let locationTouchX = locationOfTouchInLabel.x - textContainerOffset.x
    let locationTouchY = locationOfTouchInLabel.y - textContainerOffset.y
    let locationOfTouch = CGPoint(x: locationTouchX, y: locationTouchY)

    let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouch, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

    return NSLocationInRange(indexOfCharacter, targetRange)
  }
}

How to use the component

let component = RichLabelComponent(text: "Hello %@. You are meeting with %@.", buttons: ProfileRichButton(buttonTitle: "Your Name"), AttendeesRichButton(buttonTitle: "Eve and Bob"))
let view = RichLabelComponentView()
view.component = component
view.delegate = self

I can then send button, and on the delegate method I can then switch on button.self and check for case is ProfileRichButton for instance, so I can know which type of link was clicked.

The problems

What I don’t like about this solution is for instance the need to use NSString.range(of: "") to set properties. If suddenly there are two links with same name, this won’t work.

Any ideas how to improve this or restructure it in a more flexible and solid way?


Get this bounty!!!

#StackBounty: #object-oriented #swift #ios Swift/iOS component for label with clickable text buttons

Bounty: 100

So I’m creating a component based on UILabel, which I’m calling RichLabel. The main goal is to add support for clickable links (multiple). It should also be possible to to differentiate between types of links, so I can handle them differently. For instance one link should open ModalWindowA and another ModalWindowB.

I have something that works, but it’s not a very solid solution, and would love some input on the design.

RichButton

RichButton is the different types of button/links I currently support in the RichText.

protocol RichButton {
  var id: String { get }
  var buttonTitle: String { get }
}

struct ProfileRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

struct AttendeesRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

struct MeetingRichButton: RichButton {

  let id: String
  let buttonTitle: String

  init(id: String = UUID().uuidString, buttonTitle: String) {
    self.id = id
    self.buttonTitle = buttonTitle
  }

}

RichLabelComponent

RichLabelComponent is holding the data for the RichLabelComponentView and formats the text and highlights the clickable text.

protocol Component {
  var id: String { get }
}

struct RichLabelComponent: Component {

  let id: String
  let text: String
  let buttons: [RichButton]

  var formattedText: String {
    let buttonTitles = buttons.map { $0.buttonTitle }
    let formattedString = String(format: text, arguments: buttonTitles)
    return formattedString
  }

  var attributedText: NSAttributedString? {
    let attributedText = NSMutableAttributedString(string: formattedText)
    let nsFormattedText = NSString(string: formattedText)
    attributedText.setAttributes([.font: Theme.regular(size: .large)], range: formattedText.whole)
    for button in buttons {
      let range = nsFormattedText.range(of: button.buttonTitle)
      let attributedButtonTitle = NSAttributedString(string: button.buttonTitle, attributes: [.foregroundColor: Theme.secondaryOrangeColor, .font: Theme.regular(size: .large)])
      attributedText.replaceCharacters(in: range, with: attributedButtonTitle)
    }
    return attributedText
  }

  init(id: String = UUID().uuidString, text: String) {
    self.id = id
    self.text = text
    self.buttons = []
  }

  init(id: String = UUID().uuidString, text: String, buttons: RichButton...) {
    self.id = id
    self.text = text
    self.buttons = buttons
  }
}

RichLabelComponentView

This is the view responsible for displaying the formatted label, and handles the tapGesture.

protocol RichLabelComponentViewDelegate: class {
  func richLabelComponentView(_ richLabelComponentView:   RichLabelComponentView, didTapButton button: RichButton, forComponent  component: RichLabelComponent)
}

class RichLabelComponentView: UIView {

  // MARK: - Internal properties

  private lazy var label: UILabel = {
    let label = UILabel()
    label.numberOfLines = 0
    label.lineBreakMode = .byWordWrapping
    label.textAlignment = .left
    label.isUserInteractionEnabled = true
    label.translatesAutoresizingMaskIntoConstraints = false
    return label
  }()

  private lazy var tapGestureRecognizer: UITapGestureRecognizer = {
    let tapGestureRecognizer = UITapGestureRecognizer()
    tapGestureRecognizer.addTarget(self, action: #selector(tapHandler(gesture:)))
    return tapGestureRecognizer
  }()

  // MARK: - External properties

  weak var delegate: RichLabelComponentViewDelegate?

  var component: RichLabelComponent? {
    didSet {
      label.attributedText = component?.attributedText
    }
  }

  // MARK: - Setup

  override init(frame: CGRect) {
    super.init(frame: frame)
    self.addGestureRecognizer(tapGestureRecognizer)
    self.addSubviewsAndConstraints()
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }

  private func addSubviewsAndConstraints() {
    addSubview(label)

    label.leftAnchor.constraint(equalTo: self.leftAnchor).isActive = true
    label.rightAnchor.constraint(equalTo: self.rightAnchor).isActive = true
    label.topAnchor.constraint(equalTo: self.topAnchor).isActive = true
    label.bottomAnchor.constraint(equalTo: self.bottomAnchor).isActive = true
  }

  // MARK: - Actions

  @objc func tapHandler(gesture: UITapGestureRecognizer) {
    guard let component = component, let delegate = delegate else {
      return
    }

    for button in component.buttons {
      let nsString = component.formattedText as NSString
      let range = nsString.range(of: button.buttonTitle)
      if tapGestureRecognizer.didTapAttributedText(label: label, inRange: range) {
        delegate.richLabelComponentView(self, didTapButton: button, forComponent: component)
      }
    }
  }
}

This is the implementation which recognises if one of the text links/buttons where tapped:

extension UIGestureRecognizer {

  /**
   Returns `true` if the tap gesture was within the specified range of the attributed text of the label.

   - Parameter label:   The label to match tap gestures in.
   - Parameter targetRange: The range for the substring we want to match tap against.

   - Returns: A boolean value indication wether substring where tapped or not.
   */
  func didTapAttributedText(label: UILabel, inRange targetRange: NSRange) -> Bool {
    guard let attributedString = label.attributedText else { fatalError("Not able to fetch attributed string.") }

    var offsetXDivisor: CGFloat
    switch label.textAlignment {
    case .center: offsetXDivisor = 0.5
    case .right: offsetXDivisor = 1.0
    default: offsetXDivisor = 0.0
    }

    let layoutManager = NSLayoutManager()
    let textContainer = NSTextContainer(size: .zero)
    let textStorage = NSTextStorage(attributedString: attributedString)
    let labelSize = label.bounds.size

    layoutManager.addTextContainer(textContainer)
    textStorage.addLayoutManager(layoutManager)

    textContainer.lineFragmentPadding = 0.0
    textContainer.lineBreakMode = label.lineBreakMode
    textContainer.maximumNumberOfLines = label.numberOfLines
    textContainer.size = labelSize

    let locationOfTouchInLabel = self.location(in: label)
    let textBoundingBox = layoutManager.usedRect(for: textContainer)

    let offsetX = (labelSize.width - textBoundingBox.size.width) * offsetXDivisor - textBoundingBox.origin.x
    let offsetY = (labelSize.height - textBoundingBox.size.height) * 0.5 - textBoundingBox.origin.y
    let textContainerOffset = CGPoint(x: offsetX, y: offsetY)

    let locationTouchX = locationOfTouchInLabel.x - textContainerOffset.x
    let locationTouchY = locationOfTouchInLabel.y - textContainerOffset.y
    let locationOfTouch = CGPoint(x: locationTouchX, y: locationTouchY)

    let indexOfCharacter = layoutManager.characterIndex(for: locationOfTouch, in: textContainer, fractionOfDistanceBetweenInsertionPoints: nil)

    return NSLocationInRange(indexOfCharacter, targetRange)
  }
}

How to use the component

let component = RichLabelComponent(text: "Hello %@. You are meeting with %@.", buttons: ProfileRichButton(buttonTitle: "Your Name"), AttendeesRichButton(buttonTitle: "Eve and Bob"))
let view = RichLabelComponentView()
view.component = component
view.delegate = self

I can then send button, and on the delegate method I can then switch on button.self and check for case is ProfileRichButton for instance, so I can know which type of link was clicked.

The problems

What I don’t like about this solution is for instance the need to use NSString.range(of: "") to set properties. If suddenly there are two links with same name, this won’t work.

Any ideas how to improve this or restructure it in a more flexible and solid way?


Get this bounty!!!

#StackBounty: #swift #community-challenge #sprite-kit Swiftly turning wheels – The May 2017 Community Challenge

Bounty: 100

This is my attempt at the May 2017 Community Challenge in Swift, with a chain consisting of
rigid links.

I took this as an opportunity
to learn SpriteKit, Apple’s
framework for 2D games. At least Xcode 8.3.2 with Swift 3 is required to compile
the code, it runs on both macOS and iOS (instructions below).

VectorUtils.swift – Some helper methods for vector calculations.

import CoreGraphics

let π = CGFloat.pi

extension CGVector {

    init(from: CGPoint, to: CGPoint) {
        self.init(dx: to.x - from.x, dy: to.y - from.y)
    }

    func cross(_ other: CGVector) -> CGFloat {
        return dx * other.dy - dy * other.dx
    }

    var length: CGFloat {
        return hypot(dx, dy)
    }

    var arg: CGFloat {
        return atan2(dy, dx)
    }
}

Sprocket.swift – The type describing a single sprocket.

import CoreGraphics

struct Sprocket {
    let center: CGPoint
    let radius: CGFloat
    let teeth: Int

    var clockwise: Bool!
    var prevAngle: CGFloat!
    var nextAngle: CGFloat!
    var prevPoint: CGPoint!
    var nextPoint: CGPoint!

    init(center: CGPoint, radius: CGFloat) {
        self.center = center
        self.radius = radius
        self.teeth = Int((radius/2).rounded())
    }

    init(_ triplet: (x: CGFloat, y: CGFloat, r: CGFloat)) {
        self.init(center: CGPoint(x: triplet.x, y: triplet.y), radius: triplet.r)
    }

    // Normalize angles such that
    //     0 <= prevAngle < 2π
    // and
    //     prevAngle <= nextAngle < prevAngle + 2π  (if rotating counter-clockwise)
    //     prevAngle - 2π < nextAngle <= prevAngle  (if rotating clockwise)
    mutating func normalizeAngles() {
        prevAngle = prevAngle.truncatingRemainder(dividingBy: 2 * π)
        nextAngle = nextAngle.truncatingRemainder(dividingBy: 2 * π)
        while prevAngle < 0 {
            prevAngle = prevAngle + 2 * π
        }
        if clockwise {
            while nextAngle > prevAngle {
                nextAngle = nextAngle - 2 * π
            }
        } else {
            while nextAngle < prevAngle {
                nextAngle = nextAngle + 2 * π
            }
        }
    }

    mutating func computeTangentPoints() {
        prevPoint = CGPoint(x: center.x + radius * cos(prevAngle),
                            y: center.y + radius * sin(prevAngle))
        nextPoint = CGPoint(x: center.x + radius * cos(nextAngle),
                            y: center.y + radius * sin(nextAngle))
    }
}

ChainDrive.swift – The type describing the complete chain drive
system. Also contains the code to compute rotation directions,
tangent angles/points, and the length of the various segments of the
chain.

import CoreGraphics

struct ChainDrive {

    var sprockets: [Sprocket]

    var length: CGFloat!
    var period: CGFloat!
    var linkCount: Int!
    var accumLength: [(CGFloat, CGFloat)]!

    init(sprockets: [Sprocket]) {
        self.sprockets = sprockets

        computeSprocketData()
        computeChainLength()
    }

    init(_ triplets: [(CGFloat, CGFloat, CGFloat)]) {
        self.init(sprockets: triplets.map(Sprocket.init))
    }

    mutating func computeSprocketData() {

        // Compute rotation directions:
        for i in 0..<sprockets.count {
            let j = (i + 1) % sprockets.count
            let k = (j + 1) % sprockets.count

            let v1 = CGVector(from: sprockets[j].center, to: sprockets[i].center)
            let v2 = CGVector(from: sprockets[j].center, to: sprockets[k].center)
            sprockets[j].clockwise = v1.cross(v2) > 0
        }
        if !sprockets[0].clockwise {
            sprockets[1..<sprockets.count].reverse()
            for i in 0..<sprockets.count {
                sprockets[i].clockwise = !sprockets[i].clockwise
            }
        }

        // Compute tangent angles:
        for i in 0..<sprockets.count {
            let j = (i + 1) % sprockets.count

            let v = CGVector(from: sprockets[i].center, to: sprockets[j].center)
            let d = v.length
            let a = v.arg
            if sprockets[i].clockwise == sprockets[j].clockwise {
                var phi = acos((sprockets[i].radius - sprockets[j].radius)/d)
                if !sprockets[i].clockwise {
                    phi = -phi
                }
                sprockets[i].nextAngle = a + phi
                sprockets[j].prevAngle = a + phi
            } else {
                var phi = acos((sprockets[i].radius + sprockets[j].radius)/d)
                if !sprockets[i].clockwise {
                    phi = -phi
                }
                sprockets[i].nextAngle = a + phi
                sprockets[j].prevAngle = a + phi - π
            }
        }

        // Normalize angles and compute tangent points:
        for i in 0..<sprockets.count {
            sprockets[i].normalizeAngles()
            sprockets[i].computeTangentPoints()
        }
    }

    mutating func computeChainLength() {
        accumLength = []
        length = 0
        for i in 0..<sprockets.count {
            let j = (i + 1) % sprockets.count
            let l1 = length + abs(sprockets[i].nextAngle - sprockets[i].prevAngle) * sprockets[i].radius
            let l2 = l1 + CGVector(from: sprockets[i].nextPoint, to: sprockets[j].prevPoint).length
            accumLength.append((l1, l2))
            length = l2
        }

        let count = Int(length / (4 * π))
        let p1 = length / CGFloat(count)
        let p2 = length / CGFloat(count + 1)
        if abs(p1 - 4 * π) <= abs(p2 - 4 * π) {
            period = p1
            linkCount = count
        } else {
            period = p2
            linkCount = count + 1
        }

    }

    func linkCoordinatesAndPhases(offset: CGFloat) -> ([CGPoint], [CGFloat]) {
        var coords: [CGPoint] = []
        var phases: [CGFloat] = []
        var offset = offset
        var total = offset
        var i = 0

        repeat {
            let j = (i + 1) % sprockets.count
            let s: CGFloat = sprockets[i].clockwise ? -1 : 1

            var phi = sprockets[i].prevAngle + s*offset / sprockets[i].radius
            phases.append(phi)
            while total <= accumLength[i].0 && coords.count < linkCount {
                coords.append(CGPoint(x: sprockets[i].center.x + cos(phi) * sprockets[i].radius,
                                      y: sprockets[i].center.y + sin(phi) * sprockets[i].radius))
                phi += s * period / sprockets[i].radius
                total += period
            }

            var d = total - accumLength[i].0
            let v = CGVector(from: sprockets[i].nextPoint, to: sprockets[j].prevPoint)
            while total <= accumLength[i].1 && coords.count < linkCount {
                coords.append(CGPoint(x: sprockets[i].nextPoint.x + d * v.dx / v.length,
                                      y: sprockets[i].nextPoint.y + d * v.dy / v.length))
                d += period
                total += period
            }

            offset = total - accumLength[i].1
            i = j
        } while coords.count < linkCount

        return (coords, phases)
    }

}

SprocketNode.swift – Defines a SKShapeNode subclass for drawing
a single sprocket.

import SpriteKit

class SprocketNode: SKShapeNode {
    let radius: CGFloat
    let clockwise: Bool
    let teeth: Int

    init(sprocket: Sprocket) {
        self.radius = sprocket.radius
        self.clockwise = sprocket.clockwise
        self.teeth = sprocket.teeth
        super.init()

        let path = CGMutablePath()
        path.move(to: CGPoint(x: radius - 2, y: 0))
        for i in 0..<teeth {
            let a1 = π * CGFloat(4 * i - 1)/CGFloat(2 * teeth)
            let a2 = π * CGFloat(4 * i + 1)/CGFloat(2 * teeth)
            let a3 = π * CGFloat(4 * i + 3)/CGFloat(2 * teeth)
            path.addArc(center: CGPoint.zero, radius: radius - 2,
                        startAngle: a1, endAngle: a2, clockwise: false)
            path.addArc(center: CGPoint.zero, radius: radius + 2,
                        startAngle: a2, endAngle: a3, clockwise: false)
        }
        path.closeSubpath()
        self.path = path

        self.lineWidth = 0
        self.fillColor = SKColor(red: 0x86/255, green: 0x84/255, blue: 0x81/255, alpha: 1) // #868481
        self.strokeColor = .clear
        self.position = sprocket.center

        do {
            let path = CGMutablePath()
            path.addEllipse(in: CGRect(x: -3, y: -3, width: 6, height: 6))
            path.addEllipse(in: CGRect(x: -radius + 4.5, y: -radius + 4.5,
                                       width: 2 * radius - 9, height: 2 * radius - 9))
            let node = SKShapeNode(path: path)
            node.fillColor = SKColor(red: 0x64/255, green: 0x63/255, blue: 0x61/255, alpha: 1) // #646361
            node.lineWidth = 0
            node.strokeColor = .clear
            self.addChild(node)
        }
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

LinkNode.swift – Defines a SKShapeNode subclass for drawing
a linke chain link.

import SpriteKit

class LinkNode: SKShapeNode {
    static let narrowWidth: CGFloat = 2
    static let wideWidth : CGFloat = 6

    let pitch: CGFloat

    init(pitch: CGFloat) {
        self.pitch = pitch
        super.init()

        let phi = asin(LinkNode.narrowWidth / LinkNode.wideWidth)
        let path = CGMutablePath()
        path.addArc(center: CGPoint(x: -pitch/2, y: 0), radius: LinkNode.wideWidth/2,
                    startAngle: phi, endAngle: 2 * π - phi, clockwise: false)
        path.addLine(to: CGPoint(x: pitch/2, y: -LinkNode.narrowWidth/2))
        path.addArc(center: CGPoint(x: pitch/2, y: 0), radius: LinkNode.narrowWidth/2,
                    startAngle: -π/2, endAngle: π/2, clockwise: false)
        path.closeSubpath()
        self.path = path
        self.fillColor = .black
        self.lineWidth = 0
        self.strokeColor = .clear
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func moveTo(leftPin: CGPoint, rightPin: CGPoint) {
        position = CGPoint(x: (leftPin.x + rightPin.x)/2,
                           y: (leftPin.y + rightPin.y)/2)
        zRotation = CGVector(from: leftPin, to: rightPin).arg
    }

}

ChainDriveScene.swift – Defines a SKScene subclass for drawing
and animating the chain drive.

import SpriteKit

typealias Triples = [(CGFloat, CGFloat, CGFloat)]

// The system from the challenge: https://codereview.meta.stackexchange.com/a/7264 :
let system0: Triples = [(0, 0, 16), (100, 0, 16), (100, 100, 12), (50, 50, 24), (0, 100, 12)]

// Other systems from https://codegolf.stackexchange.com/q/64764:
let system1: Triples = [(0, 0, 26), (120, 0, 26)]
let system2: Triples = [(100, 100, 60), (220, 100, 14)]
let system3: Triples = [(100, 100, 16), (100, 0, 24), (0, 100, 24), (0, 0, 16)]
let system4: Triples = [(0, 0, 60), (44, 140, 16), (-204, 140, 16), (-160, 0, 60), (-112, 188, 12),
                      (-190, 300, 30), (30, 300, 30), (-48, 188, 12)]
let system5: Triples = [(0, 128, 14), (46.17, 63.55, 10), (121.74, 39.55, 14), (74.71, -24.28, 10),
                      (75.24, -103.55, 14), (0, -78.56, 10), (-75.24, -103.55, 14),
                      (-74.71, -24.28, 10), (-121.74, 39.55, 14), (-46.17, 63.55, 10)]
let system6: Triples = [(367, 151, 12), (210, 75, 36), (57, 286, 38), (14, 181, 32), (91, 124, 18),
                      (298, 366, 38), (141, 3, 52), (80, 179, 26), (313, 32, 26), (146, 280, 10),
                      (126, 253, 8), (220, 184, 24), (135, 332, 8), (365, 296, 50), (248, 217, 8),
                      (218, 392, 30)]

class ChainDriveScene: SKScene {

    let chainDrive: ChainDrive
    let chainSpeed = 16 * π // speed (points/sec)

    var initialTime: TimeInterval!
    var sprocketNodes: [SprocketNode] = []
    var linkNodes: [LinkNode] = []

    class func newScene() -> ChainDriveScene {
        let system = ChainDrive(system0)
        return ChainDriveScene(system: system)
    }

    init(system: ChainDrive) {
        self.chainDrive = system

        let minx = system.sprockets.map { $0.center.x - $0.radius }.min()! - 15
        let miny = system.sprockets.map { $0.center.y - $0.radius }.min()! - 15
        let maxx = system.sprockets.map { $0.center.x + $0.radius }.max()! + 15
        let maxy = system.sprockets.map { $0.center.y + $0.radius }.max()! + 15

        super.init(size: CGSize(width: maxx - minx, height: maxy - miny))
        self.anchorPoint = CGPoint(x: -minx/(maxx - minx), y: -miny/(maxy - miny))
        self.scaleMode = .aspectFit
    }

    required init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func setUpScene() {

        backgroundColor = .white
        sprocketNodes = chainDrive.sprockets.map(SprocketNode.init)
        for node in sprocketNodes {
            self.addChild(node)
        }

        let (coords, _) = chainDrive.linkCoordinatesAndPhases(offset: 0)
        for i in 0..<coords.count {
            let j = (i + 1) % coords.count
            let node = LinkNode(pitch: chainDrive.period)
            node.moveTo(leftPin: coords[i], rightPin: coords[j])
            self.addChild(node)
            linkNodes.append(node)
        }
    }

    override func didMove(to view: SKView) {
        self.setUpScene()
    }

    override func update(_ currentTime: TimeInterval) {
        if initialTime == nil {
            initialTime = currentTime
        }

        let distance = CGFloat(currentTime - initialTime) * chainSpeed * speed
        let k = Int(distance/chainDrive.period) % linkNodes.count
        let offset = distance.truncatingRemainder(dividingBy: chainDrive.period)

        let (coords, phases) = chainDrive.linkCoordinatesAndPhases(offset: offset)
        for i in 0..<linkNodes.count {
            let p1 = coords[i % coords.count]
            let p2 = coords[(i + 1) % coords.count]
            linkNodes[(i + linkNodes.count - k) % linkNodes.count].moveTo(leftPin: p1, rightPin: p2)
        }
        for i in 0..<phases.count {
            sprocketNodes[i].zRotation = phases[i]
        }
    }
}

The complete project is
available on GitHub.
Alternatively:

  • In Xcode 8.3.2 (or later), create a new project from the “Cross-platform SpriteKit Game” template.
  • Select “Include iOS Application” and/or “Include macOS Application”.
  • Add the above source files to the project.
  • In the GameViewController.swift files, replace
    let scene = GameScene.newGameScene()
    

    by

    let scene = ChainDriveScene.newScene()
    
  • Compile and run!

The animation runs with approx 60 frames per second both on an
1.2 GHz MacBook and on an iPhone 6s.
To give you a rough impression of what it looks like, I took a screen
recording with QuickTime Player and converted it to an animated GIF
with ffmpeg and gifsicle:

enter image description here
enter image description here

All feedback is welcome, such as (but not limited to):

  • Can the geometrical computations be simplified?
  • Better type/variable/function names?
  • There are several “implicitly unwrapped optional” properties
    in struct Sprocket. The reason is that these are computed
    (in func computeSprocketData()) after all sprockets have been
    initialized. Any suggestions how to do this two-step initialization
    more elegantly?
  • Initially I used a SKAction for rotating the sprockets, but did not
    find a way to animate the chain with SKActions. Therefore both
    sprockets and chain links are now updated in the update() method
    (which is called for each frame). Is there are better way to achieve
    the same result?
  • Another idea was to use SKAction.followPath() to animate the chain links.
    That worked well for one link, but I could not figure out how to make the
    other links follow the same path with a delay. Is that possible?
  • This is my first SpriteKit project, therefore any advice on how to
    make more idiomatic use of that framework is appreciated.


Get this bounty!!!

#StackBounty: #ios #swift #app-store #review #skstorereviewcontroller Is there a minimum time between prompts for SKStoreReviewControll…

Bounty: 100

I understand that when we call SKStoreReviewController.requestReview(), Apple decides whether to show a review prompt based on a variety of factors. What I’m curious about is the minimum time between prompts for our app. According to Apple, there is some limit (emphasis mine):

If the user hasn’t already given feedback and a request hasn’t been
made too recently
, the system displays an in-app prompt that asks for
a rating and an optional written review.

I understand that there are no guarantees about the behavior of this call beyond what’s in its documentation, but I’m wondering what happens in practice.

I’m trying to figure out whether we need to implement our own logic to wait a reasonable amount of time before requesting the prompt again, or if Apple’s definition of “recently” is good enough for this purpose.


Get this bounty!!!

#StackBounty: #ios #swift Error: Invalid Swift Support when uploading a build to iTunes Connect

Bounty: 150

I’m submitting my first build for TestFlight distribution and am getting the following error. Bitcode is turned off. Other answers seem decently old so figured I’d re-ask for 2018.

Invalid Swift Support – The files libswiftDarwin.dylib, libswiftMetal.dylib, libswiftCoreAudio.dylib, libswiftsimd.dylib, libswiftQuartzCore.dylib, libswiftos.dylib, libswiftObjectiveC.dylib, libswiftDispatch.dylib, libswiftCoreGraphics.dylib, libswiftCoreFoundation.dylib, libswiftUIKit.dylib, libswiftCoreMedia.dylib, libswiftAVFoundation.dylib, libswiftCore.dylib, libswiftFoundation.dylib, libswiftCoreImage.dylib aren’t at the expected location /Payload/MyApp.app/Frameworks. Move the file to the expected location, rebuild your app using the current public (GM) version of Xcode, and resubmit it.

I have a .dylib file that I compile for my project that lives at ./MyProj/Core/lib.dylib. I saw this browsing around as well

It’s likely you have a plain dylib outside of a framework somewhere, which is only supported on macOS. Please review the Troubleshooting section of Tech Note 2435 for more context.

If having a bare dylib is indeed the cause of the problem, please file a bug for an error message that clearly explains this.


Get this bounty!!!

#StackBounty: #ios #swift #uisearchbar #uisearchcontroller #uisearchresultscontroller UISearchBar's height is incorrect when presen…

Bounty: 50

I have a search bar in tableView’s header view, and I don’t change the height of it(default is 56pt). When search bar is active and its position goes top of the screen, it’s height becomes 50pt. It will both happen on iPhone or simulator, version iOS 11.

First appear:

<UISearchBar: 0x7f94b6646900; frame = (0 0; 375 56); text = ''; gestureRecognizers = <NSArray: 0x60400025fc20>; layer = <CALayer: 0x604000225fe0>>

Editing:

<UISearchBar: 0x7f94b6646900; frame = (0 14; 375 50); text = ''; autoresize = W+TM; gestureRecognizers = <NSArray: 0x60400025fc20>; layer = <CALayer: 0x604000225fe0>>

It results in a weird gap between the search bar and result controller’s view.
gap

Thanks for any ideas.

Edit1:
Here’s my code:

fileprivate func configSearchBar() -> Void {
    let bar = self.searchBar
    var size = bar.frame.size

    let statusBarHeight = UIApplication.shared.statusBarFrame.size.height
    bar.setBackgroundImage(UIImage.imageWith(color: .white, size: size),
                           for: .any,
                           barMetrics: .default)
    let colorWhenEditing = UIColor.colorFromHexString("#FAFAFA")
    if UIDevice.isIPHONEX() {
        bar.barTintColor = colorWhenEditing
    } else {
        bar.setBackgroundImage(UIImage.imageWith(color: colorWhenEditing, size: CGSize(width: size.width, height: size.height + statusBarHeight)),
                               for: .topAttached,
                               barMetrics: .default)
    }
    size = CGSize(width: bar.frame.size.width - 30, height: 30)
    let image = UIImage.imageWith(color: UIColor(hexString: "#F0F0F0"), size: size)?.zoom(toSize: size, cornerRadius: 4)
    bar.setSearchFieldBackgroundImage(image, for: .normal)

    bar.searchTextPositionAdjustment = UIOffset(horizontal: 5, vertical: 0)
}

self.tableView.tableHeaderView = self.searchBar

Edit2: – Search controller and search bar initialization:

// Main view controller
lazy var searchResultController: UISearchController = {
    let vc = MailSearchResultViewController(nibName: nil, bundle: nil)
    let sc = UISearchController(searchResultsController: vc)
    sc.searchResultsUpdater = self
    sc.modalPresentationCapturesStatusBarAppearance = true
    sc.delegate = self
    return sc
}()
var searchBar: UISearchBar {
    return searchResultController.searchBar
}
// MailSearchResultViewController - viewDidLoad:
self.edgesForExtendedLayout = []
self.automaticallyAdjustsScrollViewInsets = false


Get this bounty!!!

#StackBounty: #swift #uinavigationbar #uisplitviewcontroller UISplitView's MasterViewController and Navigation Issue

Bounty: 50

I am updating my existing app to include a SplitView for iPads.

I have it working with a UITabBar, but am having an issue with my masterViewController as it is generating a “duplicate” navigation bar that is covering my existing navigation items on all masterViewControllers (tabs), including searchBar on the search tab.

The code I have is:

AppDelegate

class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool
{

    let splitViewController = self.window!.rootViewController as! UISplitViewController
    splitViewController.delegate = self
    splitViewController.preferredPrimaryColumnWidthFraction = 0.33
    splitViewController.minimumPrimaryColumnWidth = 375
    splitViewController.preferredDisplayMode = .allVisible

    return true
}

func splitViewController(_ splitViewController: UISplitViewController, collapseSecondary secondaryViewController:UIViewController, onto primaryViewController:UIViewController) -> Bool {
    return true
}

The reason for having this in the AppDelegate is I saw an example where placing it hear will allow me not to require the code in each of the different Master Views (each tab). Have yet to test this as still working on the first master view.

Master View

override func viewDidLoad()

{

    self.extendedLayoutIncludesOpaqueBars = true

    self.navigationItem.hidesBackButton = true

    // 3D Touch
    if traitCollection.forceTouchCapability == .available {
        registerForPreviewing(with: self as UIViewControllerPreviewingDelegate, sourceView: view)
        ThreeDTouch = true
    }

    self.addSwitchVewButtonToNavigationBar()
    self.addCategoryButtonToNavigationBar()

}

func addSwitchVewButtonToNavigationBar() {
    let switchButton = UIButton(type: UIButtonType.custom)

    let editImage = UIImage(named: "CollectionButton")?.withRenderingMode(.alwaysTemplate)
    switchButton.setImage(editImage, for: .normal)
    switchButton.addTarget(self, action: #selector(SpeciesViewController.onSwitchView), for: UIControlEvents.touchUpInside)
    let switchButtonFinal = UIBarButtonItem(customView:switchButton)

    self.navigationItem.rightBarButtonItem = switchButtonFinal

}

@IBAction func onSwitchView(_ sender: UIBarButtonItem)
{
    AppDelegate.getAppState().isListViewSelected = false

    let speciesColletion = storyboard?.instantiateViewController(withIdentifier: Resource.SpeciesCollectionStoryboard) as! SpeciesCollectionViewController
    self.navigationController?.viewControllers = [speciesColletion]
}

Originally, the onSwitchViewButton was embedded using the IB, but did not work. This is the same system used for the addFavorite on the Detail View.

enter image description here
enter image description here


Get this bounty!!!

#StackBounty: #ios #swift #sirikit #ios-contacts iOS Contact app Interaction

Bounty: 50

I’m trying to get a messaging app integrated with the iOS Contacts app, so that users can initiate messages via the app directly from Contacts. This was covered in WWDC 2016 session 240 but apparently some details were omitted.

Following the WWDC example, I have:

  • Added the activity type to the app’s Info.plist:
    <key>NSUserActivityTypes</key>
    <array>
            <string>INSendMessageIntent</string>
    </array>
    
  • Implemented application(_:continue:restorationHandler:) in my app delegate.

  • Created and donated an interaction
    let activity = NSUserActivity(activityType: "com.example.message")  
    activity.title = "Send CB Test Message"  
    activity.expirationDate = Date.distantFuture  
    
    let recipient = INPerson( /* recipient with an email address in my Contacts database */ )  
    let sender = INPerson( /* me */ )        
    
    let intent = INSendMessageIntent(recipients: [recipient], content: nil, groupName: nil, serviceName: "CB Test Chat", sender: sender)  
    
    let interaction = INInteraction(intent: intent, response: nil)  
    interaction.direction = .outgoing  
    interaction.donate { (error) in  
        print("Donated")  
        if let error = error {  
            print("Donate error: (error)")  
        }  
    }  
    

This sort of works. The app shows up as an option on the one recipient’s card in Contacts. Tapping it in Contacts launches my app with an NSUserActivity. That’s good but it’s not enough.

The WWDC session used WhatsApp as an example. WhatsApp shows up as an option on all of my contacts, even those without WhatsApp accounts. I thought maybe WhatsApp had created and donated interactions for everyone. But if I create a new contact while WhatsApp isn’t running, it’s immediately an option on that contact. I experimented a little, setting the recipient argument to nil or to an empty array, but that had no effect.

So what am I missing here? I’m close, maybe? But it seems like donating interactions might not be what I actually need.


Get this bounty!!!

#StackBounty: #ios #swift #terrain #srtm #hgt Creating Terrain Map with SRTM HGT File

Bounty: 500

I am working on an iOS application. Where I show the Elevation and Topography map of a certain area. I have managed to download the .hgt file within app from here.

So far I am able to extract the Elevation from the hgt file. Now I have to also show the Terrain Map for that area. I have been searching about it and I think I can’t create terrain map directly with hgt file within iOS application. I have to use GRASS GIS, SRTM2OSM or TileMill to create terrain map and then use it in application.

Can please anyone direct me what I can do here and how to proceed.


Get this bounty!!!